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/21 12:42:12 UTC

[qpid-dispatch] branch eallen-DISPATCH-1385 updated: Latest changes

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 6ebfb4e  Latest changes
6ebfb4e is described below

commit 6ebfb4e0a2b08add6cf9c9c4039708cb5c8692f8
Author: Ernest Allen <ea...@redhat.com>
AuthorDate: Thu Nov 21 07:41:50 2019 -0500

    Latest changes
---
 .gitignore                                         |    3 +
 LICENSE                                            |    6 -
 console/CMakeLists.txt                             |  119 +-
 console/react/package.json                         |   33 +-
 console/react/src/App.css                          |   26 +
 console/react/src/App.js                           |   40 +-
 console/react/src/App.test.js                      |   26 +-
 console/react/src/DropdownMenu.test.js             |   20 -
 console/react/src/addressesComponent.test.js       |   36 -
 console/react/src/alertList.test.js                |   30 -
 console/react/src/amqp/connection.js               |  354 --
 console/react/src/brokers.ttf                      |    0
 console/react/src/chord/chordToolbar.js            |   25 +-
 console/react/src/chord/chordToolbar.test.js       |   19 +
 console/react/src/chord/chordViewer.js             |    2 +-
 console/react/src/chord/chordViewer.test.js        |   32 +-
 console/react/src/chord/legendComponent.js         |    2 +-
 console/react/src/chord/optionsComponent.js        |    2 +-
 console/react/src/{ => common}/DropdownMenu.js     |    4 +-
 .../addressData.js => common/DropdownMenu.test.js} |   42 +-
 .../react/src/{ => common}/addressesComponent.js   |    0
 .../react/src/common/addressesComponent.test.js    |   50 +
 console/react/src/common/amqp/connection.js        |  385 ++
 console/react/src/{ => common}/amqp/correlator.js  |   40 +-
 console/react/src/{ => common}/amqp/management.js  |   35 +-
 console/react/src/{ => common}/amqp/topology.js    |  139 +-
 console/react/src/{ => common}/amqp/utilities.js   |   49 +-
 console/react/src/{ => common}/connectionClose.js  |    0
 console/react/src/common/connectionClose.test.js   |   58 +
 .../contextMenu.test.js}                           |   44 +-
 .../react/src/{ => common}/contextMenuComponent.js |   31 +-
 console/react/src/{ => common}/dropdownPanel.js    |   48 +-
 console/react/src/common/pleaseWait.js             |   58 +
 console/react/src/{ => common}/qdrGlobals.js       |    0
 .../react/src/{updated.js => common/qdrPopup.js}   |   12 +-
 .../src/{updated.js => common/qdrPopup.test.js}    |   31 +-
 console/react/src/{ => common}/qdrService.js       |   51 +-
 console/react/src/{ => common}/tableToolbar.js     |    0
 console/react/src/common/tableToolbar.test.js      |   46 +
 console/react/src/{ => common}/updated.js          |    0
 .../src/{updated.js => common/updated.test.js}     |   32 +-
 console/react/src/connect.test.js                  |   31 -
 console/react/src/{ => connect}/connect-form.js    |  112 +-
 console/react/src/connect/connect-form.test.js     |   51 +
 console/react/src/connect/connectPage.js           |  100 +
 .../{updated.js => connect/connectPage.test.js}    |   28 +-
 console/react/src/connectPage.js                   |   69 -
 console/react/src/connectPage.test.js              |   12 -
 console/react/src/connectionClose.test.js          |   42 -
 console/react/src/contextMenu.test.js              |   23 -
 console/react/src/details/createTablePage.test.js  |   57 +-
 .../react/src/details/dataSources/addressData.js   |    2 +-
 .../src/details/dataSources/connectionData.js      |    2 +-
 .../react/src/details/dataSources/defaultData.js   |   38 +-
 .../react/src/{ => details}/detailsTablePage.js    |   45 +-
 .../addressData.js => detailsTablePage.test.js}    |   39 +-
 .../details/{enitiesPage.js => entitiesPage.js}    |   36 +-
 console/react/src/details/entitiesPage.test.js     |   50 +
 console/react/src/details/entityList.js            |    3 +-
 .../connectionData.js => entityList.test.js}       |   45 +-
 console/react/src/details/entityListTable.js       |   16 +-
 console/react/src/details/routerSelect.js          |   26 +-
 .../react/src/details/schema/schemaPage.test.js    |   21 +-
 console/react/src/details/updateTablePage.js       |   91 +-
 console/react/src/details/updateTablePage.test.js  |   43 +-
 console/react/src/detailsTablePage.test.js         |   19 -
 console/react/src/index.css                        |   13 -
 console/react/src/index.js                         |   26 +-
 console/react/src/layout.test.js                   |   33 -
 console/react/src/notificationDrawer.test.js       |   34 -
 .../src/overview/dashboard/activeAddressesCard.js  |   39 +-
 .../src/{ => overview/dashboard}/alertList.js      |    0
 .../react/src/overview/dashboard/alertList.test.js |   47 +
 console/react/src/overview/dashboard/chartBase.js  |   72 +-
 .../dashboard/{chartBase.js => chartData.js}       |   63 +-
 .../react/src/overview/dashboard/dashboardPage.js  |   10 +-
 .../src/overview/dashboard/dashboardPage.test.js   |   42 +-
 .../overview/dashboard/delayedDeliveriesCard.js    |   55 +-
 .../src/overview/dashboard/inflightChart.test.js   |   40 +-
 .../react/src/overview/dashboard/inflightData.js   |   61 +
 .../react/src/{ => overview/dashboard}/layout.js   |  211 +-
 .../react/src/overview/dashboard/layout.test.js    |   76 +
 .../{ => overview/dashboard}/notificationDrawer.js |   40 +-
 .../overview/dashboard/notificationDrawer.test.js  |   50 +
 .../src/overview/dashboard/throughputChart.js      |   18 -
 .../src/overview/dashboard/throughputChart.test.js |   40 +-
 .../{throughputChart.js => throughputData.js}      |   23 +-
 .../src/overview/dataSources/connectionData.js     |   10 +-
 console/react/src/overview/overviewPage.js         |    2 +-
 console/react/src/overview/overviewPage.test.js    |   89 +-
 console/react/src/overview/overviewTable.js        |   16 +-
 console/react/src/overview/overviewTable.test.js   |   19 -
 console/react/src/pleaseWait.js                    |   51 -
 console/react/src/qdrPopup.js                      |   14 -
 console/react/src/qdrPopup.test.js                 |   15 -
 console/react/src/{updated.js => serviceTest.js}   |   25 +-
 console/react/src/serviceWorker.js                 |  135 -
 console/react/src/setupTests.js                    |   22 +-
 console/react/src/tableToolbar.test.js             |   29 -
 console/react/src/topology/clientInfoComponent.js  |    2 +-
 .../clientInfoDetailsComponent.js}                 |   22 +-
 .../src/topology/clientInfoDetailsComponent.jsx    |   24 -
 console/react/src/topology/contextMenu.js          |    2 +-
 console/react/src/topology/legend.js               |   42 +-
 console/react/src/topology/links.js                |   30 +-
 console/react/src/topology/nodes.js                |    2 +-
 console/react/src/topology/svgUtils.js             |   33 +-
 console/react/src/topology/topoUtils.js            |   61 +-
 console/react/src/topology/topologyPage.test.js    |   21 +-
 console/react/src/topology/topologyToolbar.js      |   31 +-
 console/react/src/topology/topologyToolbar.test.js |   44 +-
 console/react/src/topology/topologyViewer.js       |    6 +-
 console/react/src/topology/topologyViewer.test.js  |   40 +-
 console/react/src/topology/traffic.js              |   40 +-
 console/react/src/topology/trafficComponent.js     |   16 +-
 console/react/src/updated.test.js                  |   16 -
 .../react/{src => test_data}/qdrService.mock.js    |   12 +-
 console/react/yarn.lock                            | 4799 ++++----------------
 tests/system_test.py                               |    4 +-
 tests/system_tests_console.py                      |  183 +-
 120 files changed, 3658 insertions(+), 6123 deletions(-)

diff --git a/.gitignore b/.gitignore
index b4dc769..9a2c2c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,6 @@ console/stand-alone/dist/
 console/stand-alone/package-lock.json
 tools/qdmanage
 tools/qdstat
+console/react/build/
+console/react/coverage/
+
diff --git a/LICENSE b/LICENSE
index 66380af..c620013 100644
--- a/LICENSE
+++ b/LICENSE
@@ -212,12 +212,6 @@ The following 3 files are licensed under the BSD license, for details see licens
 2. console/hawtio/src/main/webapp/plugin/lib/d3.min.js
 3. console/hawtio/src/main/webapp/plugin/lib/zd3-queue.min.js
 
-console/dispatch-dashboard/dispatch/static/dashboard/dispatch/jquery.dynatree.min.js is licensed under the MIT licence, for details see licenses/jquery.dynatree-license
-
-The following 2 files are covered by the MIT License, for details see licenses/angular-ui-license
-1. console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/slider.js
-2. console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/ui-grid.js
-
 The following 2 files are covered by the MIT license, for details see licenses/jquery.tipsy-license
 1. console/hawtio/src/main/webapp/plugin/css/jquery.tipsy.css
 2. console/hawtio/src/main/webapp/plugin/lib/jquery.tipsy.js
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
index 07b4a5f..749cff4 100644
--- a/console/CMakeLists.txt
+++ b/console/CMakeLists.txt
@@ -30,37 +30,51 @@ if(CONSOLE_INSTALL)
         OUTPUT_VARIABLE NPM_VERSION)
     if(NOT (${NPM_VERSION} VERSION_LESS "3.1.10"))
 
-          set(CONSOLE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/console/stand-alone")
+          set(CONSOLE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/console/react")
           set(CONSOLE_BUILD_DIR "${CMAKE_BINARY_DIR}/console")
 
           ## copy the build config files
           configure_file( ${CONSOLE_SOURCE_DIR}/package.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
-          configure_file( ${CONSOLE_SOURCE_DIR}/package-lock.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
-          configure_file( ${CONSOLE_SOURCE_DIR}/tslint.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
-          configure_file( ${CONSOLE_SOURCE_DIR}/gulpfile.js ${CONSOLE_BUILD_DIR}/ COPYONLY)
-          configure_file( ${CONSOLE_SOURCE_DIR}/vendor-js.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
-          configure_file( ${CONSOLE_SOURCE_DIR}/vendor-css.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
-
-          ## Files needed to create the ${CONSOLE_ARTIFACTS}
-          file (GLOB_RECURSE CONSOLE_JS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.js)
-          file (GLOB_RECURSE CONSOLE_TS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.ts)
-          file (GLOB CONSOLE_TXT_SOURCES ${CONSOLE_BUILD_DIR}/vendor-*.txt)
-          set(CONSOLE_CSS_SOURCE ${CONSOLE_SOURCE_DIR}/plugin/css/dispatch.css)
-          set(ALL_CONSOLE_SOURCES ${CONSOLE_CSS_SOURCE} ${CONSOLE_TXT_SOURCES})
-
-          ## Files created during the console build
+          configure_file( ${CONSOLE_SOURCE_DIR}/yarn.lock ${CONSOLE_BUILD_DIR}/ COPYONLY)
+
+          function(copyDirectory fromDir toDir subDir)
+            set(BASE "${fromDir}/${subDir}")
+            file (GLOB_RECURSE FILES ${BASE}/*.*)
+            foreach(ITEM ${FILES})
+              get_filename_component(ITEM_PATH ${ITEM} PATH)
+              get_filename_component(ITEM_FILE ${ITEM} NAME)
+              string(REPLACE ${BASE} "" ITEM_PATH ${ITEM_PATH})
+              #message(STATUS "from ${ITEM} to ${toDir}/${subDir}${ITEM_PATH}/${ITEM_FILE}")
+              configure_file( ${ITEM} "${toDir}/${subDir}${ITEM_PATH}/${ITEM_FILE}" COPYONLY)
+            endforeach()
+          endfunction()
+
+          # copy the files needed for the build to the build/console directory
+          # this is needed because 'npm run build' and 'npm test' create and modify
+          # files and we don't want that to happen in the source directory
+          copyDirectory(${CONSOLE_SOURCE_DIR} ${CONSOLE_BUILD_DIR} "src")
+          copyDirectory(${CONSOLE_SOURCE_DIR} ${CONSOLE_BUILD_DIR} "public")
+          copyDirectory(${CONSOLE_SOURCE_DIR} ${CONSOLE_BUILD_DIR} "test_data")
+
+          ## If any of these files change, rebuild the console
+          file (GLOB_RECURSE CONSOLE_SOURCES ${CONSOLE_SOURCE_DIR}/src/*.*)
+          file (GLOB_RECURSE CONSOLE_PUBLIC_FILES ${CONSOLE_SOURCE_DIR}/public/*.*)
+          file (GLOB_RECURSE CONSOLE_TEST_FILES ${CONSOLE_SOURCE_DIR}/test_data/*.*)
+          set(ALL_CONSOLE_SOURCES ${CONSOLE_SOURCES} ${CONSOLE_PUBLIC_FILES} ${CONSOLE_TEST_FILES})
+
+          ## If any of these files is older than the above ALL_CONSOLE_SOURCE
+          ## then rebuild the console
           set(CONSOLE_ARTIFACTS
-            ${CONSOLE_BUILD_DIR}/dist/js/vendor.min.js
-            ${CONSOLE_BUILD_DIR}/dist/css/vendor.min.css
-            ${CONSOLE_BUILD_DIR}/dist/css/dispatch.min.css
+            ${CONSOLE_BUILD_DIR}/build/index.html
+            ${CONSOLE_BUILD_DIR}/build/app.js
           )
 
           ## Tell cmake how and when to build ${CONSOLE_ARTIFACTS}
           add_custom_command (
             OUTPUT ${CONSOLE_ARTIFACTS}
             COMMENT "Running console build"
-            COMMAND npm install --loglevel=error
-            COMMAND ${NPM_EXECUTABLE} run build -- --src ${CONSOLE_SOURCE_DIR} --target "production"
+            COMMAND ${NPM_EXECUTABLE} install --loglevel=error
+            COMMAND ${NPM_EXECUTABLE} run build -- --target "production"
             DEPENDS ${ALL_CONSOLE_SOURCES}
             WORKING_DIRECTORY ${CONSOLE_BUILD_DIR}/
             )
@@ -75,72 +89,11 @@ if(CONSOLE_INSTALL)
           ##
 
           ## Files copied to the root of the console's install dir
-          set(BASE_FILES
-            ${CONSOLE_SOURCE_DIR}/index.html
-            ${CONSOLE_SOURCE_DIR}/main.js
-            ${CONSOLE_SOURCE_DIR}/favicon-32x32.png
-            ${CONSOLE_SOURCE_DIR}/config.json
-          )
-          ## Files copied to the img/ dir
-          set(IMAGES
-            ${CONSOLE_SOURCE_DIR}/plugin/img/bg-modal-about-pf.png
-            ${CONSOLE_SOURCE_DIR}/plugin/img/logo-alt.svg
-            ${CONSOLE_SOURCE_DIR}/plugin/img/console_logo.png
-          )
-          ## Files copied to the css/ dir
-          set(CSS_FONTS
-            ${CONSOLE_SOURCE_DIR}/plugin/css/brokers.ttf
-          )
-          ## Files copied to the css/fonts/ dir
-          set(CSSFONTS_FONTS
-            ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/fonts/ui-grid.woff
-            ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/fonts/ui-grid.ttf
-          )
-          ## Files copied to the fonts/ dir
-          set(VENDOR_FONTS
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-SemiboldItalic-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-BoldItalic-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Italic-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.woff2
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.eot
-            ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf
-            ${CONSOLE_BUILD_DIR}/node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
-          )
-
-          install(DIRECTORY ${CONSOLE_BUILD_DIR}/dist/
+          install(DIRECTORY ${CONSOLE_BUILD_DIR}/build/
             DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
             PATTERN "*.map" EXCLUDE
           )
-          install(DIRECTORY ${CONSOLE_SOURCE_DIR}/plugin/html/
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/html
-            FILES_MATCHING PATTERN "*.html"
-          )
-          install(DIRECTORY ${CONSOLE_SOURCE_DIR}/plugin/js/
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/plugin/js
-            FILES_MATCHING PATTERN "*.js"
-          )
-          install(FILES ${BASE_FILES}
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
-          )
-          install(FILES ${CSS_FONTS}
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/
-          )
-          install(FILES ${CSSFONTS_FONTS}
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/fonts/
-          )
-          install(FILES ${VENDOR_FONTS}
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/fonts/
-          )
-          install(FILES ${IMAGES}
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/img/
-          )
-          install(DIRECTORY ${CONSOLE_SOURCE_DIR}/plugin/data/
-            DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/plugin/data
-          )
+
     else(NOT (${NPM_VERSION} VERSION_LESS "3.1.10"))
       message(STATUS "Cannot build console. npm version 3.1.10 or greater is required.")
     endif(NOT (${NPM_VERSION} VERSION_LESS "3.1.10"))
diff --git a/console/react/package.json b/console/react/package.json
index 74802e8..033aebf 100644
--- a/console/react/package.json
+++ b/console/react/package.json
@@ -4,32 +4,24 @@
   "private": true,
   "dependencies": {
     "@patternfly/patternfly": "^2.13.0",
-    "@patternfly/react-charts": "^4.1.5",
+    "@patternfly/react-charts": "^5.1.6",
     "@patternfly/react-core": "^3.38.1",
     "@patternfly/react-icons": "^3.10.4",
     "@patternfly/react-styles": "^3.3.3",
     "@patternfly/react-table": "^2.11.1",
     "@patternfly/react-topology": "^2.7.31",
-    "@react-mock/localstorage": "^0.1.2",
-    "@testing-library/jest-dom": "^4.2.3",
-    "@testing-library/react": "^9.3.2",
-    "body-parser": "^1.19.0",
+    "d3": "^3.5.17",
     "d3-queue": "^3.0.7",
-    "eslint-plugin-patternfly-react": "^0.2.3",
     "express": "^4.17.1",
-    "jest-axe": "^3.2.0",
-    "lodash-es": "^4.17.11",
-    "patternfly-react": "^2.36.1",
-    "prettier": "^1.18.2",
+    "patternfly": "^3.59.4",
     "prop-types": "^15.7.2",
-    "react": "^16.8.6",
-    "react-dom": "^16.8.6",
+    "react": "^16.12.0",
+    "react-dom": "^16.12.0",
+    "react-fontawesome": "^1.7.1",
     "react-router-dom": "^5.0.1",
-    "react-scripts": "3.0.1",
-    "redux": "^4.0.1",
+    "react-scripts": "^3.2.0",
     "rhea": "^1.0.8",
-    "topojson-client": "^3.0.1",
-    "typescript": "^3.5.2"
+    "topojson-client": "^3.0.1"
   },
   "scripts": {
     "start": "react-scripts start",
@@ -51,5 +43,14 @@
       "last 1 firefox version",
       "last 1 safari version"
     ]
+  },
+  "devDependencies": {
+    "jest-axe": "^3.2.0",
+    "eslint-plugin-patternfly-react": "^0.2.3",
+    "@react-mock/localstorage": "^0.1.2",
+    "@testing-library/jest-dom": "^4.2.3",
+    "@testing-library/react": "^9.3.2",
+    "body-parser": "^1.19.0",
+    "prettier": "^1.19.1"
   }
 }
diff --git a/console/react/src/App.css b/console/react/src/App.css
index a71287c..2f0581f 100644
--- a/console/react/src/App.css
+++ b/console/react/src/App.css
@@ -1,3 +1,22 @@
+/*
+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.
+*/
+
 #root {
   height: 100vh;
 }
@@ -379,6 +398,13 @@ div.state-container button.pf-c-clipboard-copy__group-copy {
   letter-spacing: 0.3em;
 }
 
+.pf-c-content h2.connect-description {
+  font-size: 16px;
+  margin-top: 2em;
+  margin-bottom: 0;
+  padding: 0;
+}
+
 .overview-title::first-letter {
   text-transform: uppercase;
 }
diff --git a/console/react/src/App.js b/console/react/src/App.js
index d3ba9f8..dbcafa8 100644
--- a/console/react/src/App.js
+++ b/console/react/src/App.js
@@ -1,22 +1,52 @@
+/*
+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.
+*/
+
 import React, { Component } from "react";
+import { HashRouter as Router, Route } from "react-router-dom";
+
 import "@patternfly/patternfly/patternfly.css";
 import "@patternfly/patternfly/patternfly-addons.css";
 
 import "patternfly/dist/css/patternfly.css";
 import "patternfly/dist/css/patternfly-additions.css";
 import "@patternfly/patternfly/components/Nav/nav.css";
-import { QDRService } from "./qdrService";
+import { QDRService } from "./common/qdrService";
 import "./App.css";
-import PageLayout from "./layout";
+import PageLayout from "./overview/dashboard/layout";
 class App extends Component {
   state = {};
 
   render() {
+    // service is passed in to make testing easier
     const service = new QDRService();
+    // also, a router is used here to provide PageLayout with a history property
     return (
-      <div className="App pf-m-redhat-font">
-        <PageLayout service={service} config={this.props.config} />
-      </div>
+      <Router>
+        <div className="App pf-m-redhat-font">
+          <Route
+            path="/"
+            render={props => (
+              <PageLayout service={service} {...props} config={this.props.config} />
+            )}
+          />
+        </div>
+      </Router>
     );
   }
 }
diff --git a/console/react/src/App.test.js b/console/react/src/App.test.js
index 18c16e5..3d23f5a 100644
--- a/console/react/src/App.test.js
+++ b/console/react/src/App.test.js
@@ -1,14 +1,30 @@
+/*
+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.
+*/
+
 import React from "react";
 import { render } from "@testing-library/react";
-import { axe, toHaveNoViolations } from "jest-axe";
-
+import { axe } from "jest-axe";
 import App from "./App";
 
-expect.extend(toHaveNoViolations);
-
 const title = "Apache Qpid Dispatch Console";
 const config = { title };
-it("renders the correct title without accessibility violations", async () => {
+it("renders the main page with the correct title and without accessibility violations", async () => {
   const { container, getAllByText } = render(<App config={config} />);
 
   const results = await axe(container);
diff --git a/console/react/src/DropdownMenu.test.js b/console/react/src/DropdownMenu.test.js
deleted file mode 100644
index 995b26d..0000000
--- a/console/react/src/DropdownMenu.test.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import DropdownMenu from "./DropdownMenu";
-
-it("the dropdown menu component renders and calls event handlers", () => {
-  const isVisible = true;
-  const isConnected = () => true;
-  let logoutCalled = false;
-  const handleDropdownLogout = () => logoutCalled = true
-  let menuRef = null;
-  render(
-    <DropdownMenu
-      ref={el => (menuRef = el)}
-      isVisible={isVisible} isConnected={isConnected}
-      handleDropdownLogout={handleDropdownLogout} />
-  );
-  menuRef.show(true)
-  menuRef.logout()
-  expect(logoutCalled).toBe(true)
-});
diff --git a/console/react/src/addressesComponent.test.js b/console/react/src/addressesComponent.test.js
deleted file mode 100644
index fffe287..0000000
--- a/console/react/src/addressesComponent.test.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from "react";
-import { render, fireEvent } from '@testing-library/react';
-import AddressesComponent from "./addressesComponent";
-
-it("renders the addresses component with an address", () => {
-  let handleChangeAddressCalled = false;
-  let handleHoverAddress = undefined;
-  const props = {
-    addresses: { test: true },
-    addressColors: { test: "#EAEAEA" },
-    handleChangeAddress: () => handleChangeAddressCalled = true,
-    handleHoverAddress: (address, over) => handleHoverAddress = over
-  }
-  const { getByLabelText } = render(
-    <AddressesComponent
-      {...props} />);
-  const node = getByLabelText("colored dot");
-  fireEvent.click(node)
-  expect(handleChangeAddressCalled).toBe(true)
-  fireEvent.mouseOver(node);
-  expect(handleHoverAddress).toBe(true)
-  fireEvent.mouseOut(node);
-  expect(handleHoverAddress).toBe(false)
-});
-
-it("renders the addresses component without an address", () => {
-  const props = {
-    addresses: {},
-    addressColors: {},
-  }
-  const { getByText } = render(
-    <AddressesComponent
-      {...props} />);
-  expect(getByText("There is no traffic")).toBeInTheDocument();
-});
-
diff --git a/console/react/src/alertList.test.js b/console/react/src/alertList.test.js
deleted file mode 100644
index f05c93d..0000000
--- a/console/react/src/alertList.test.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import AlertList from "./alertList";
-
-it("renders the AlertList component", () => {
-  let ref = null;
-  const props = {
-  }
-  const { getByLabelText, queryByLabelText } = render(
-    <AlertList
-      ref={el => (ref = el)}
-      {...props} />)
-  // the container should be there
-  expect(getByLabelText("alert-list")).toBeInTheDocument();
-  // there should be no alerts in the list to start with
-  expect(queryByLabelText("alert-close-button")).toBeNull();
-
-  // add an alert
-  ref.addAlert("info", "testing");
-  // the alert close button should now be there
-  expect(getByLabelText("alert-close-button")).toBeInTheDocument();
-
-  const alert = {
-    key: 0
-  }
-  // hide the alert
-  ref.hideAlert(alert);
-  // the alert close button should now be gone
-  expect(queryByLabelText("alert-close-button")).toBeNull();
-});
diff --git a/console/react/src/amqp/connection.js b/console/react/src/amqp/connection.js
deleted file mode 100644
index 97ff56f..0000000
--- a/console/react/src/amqp/connection.js
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright 2017 Red Hat Inc.
- *
- * Licensed 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.
- */
-/* global Promise */
-import Correlator from "./correlator.js";
-
-const rhea = require("rhea");
-//import { on, websocket_connect, removeListener, once, connect } from 'rhea';
-
-class ConnectionManager {
-  constructor(protocol) {
-    this.sender = undefined;
-    this.receiver = undefined;
-    this.connection = undefined;
-    this.version = undefined;
-    this.errorText = undefined;
-    this.protocol = protocol;
-    this.schema = undefined;
-    this.connectActions = [];
-    this.disconnectActions = [];
-    this.correlator = new Correlator();
-    this.on_message = function (context) {
-      this.correlator.resolve(context);
-    }.bind(this);
-    this.on_disconnected = function () {
-      this.errorText = "Disconnected";
-      this.executeDisconnectActions(this.errorText);
-    }.bind(this);
-    this.on_connection_open = function () {
-      this.executeConnectActions();
-    }.bind(this);
-  }
-  versionCheck(minVer) {
-    var verparts = this.version.split(".");
-    var minparts = minVer.split(".");
-    try {
-      for (var i = 0; i < minparts.length; ++i) {
-        if (parseInt(minVer[i] > parseInt(verparts[i]))) return false;
-      }
-    } catch (e) {
-      return false;
-    }
-    return true;
-  }
-  addConnectAction(action) {
-    if (typeof action === "function") {
-      this.delConnectAction(action);
-      this.connectActions.push(action);
-    }
-  }
-  addDisconnectAction(action) {
-    if (typeof action === "function") {
-      this.delDisconnectAction(action);
-      this.disconnectActions.push(action);
-    }
-  }
-  delConnectAction(action) {
-    if (typeof action === "function") {
-      var index = this.connectActions.indexOf(action);
-      if (index >= 0) this.connectActions.splice(index, 1);
-    }
-  }
-  delDisconnectAction(action) {
-    if (typeof action === "function") {
-      var index = this.disconnectActions.indexOf(action);
-      if (index >= 0) this.disconnectActions.splice(index, 1);
-    }
-  }
-  executeConnectActions() {
-    this.connectActions.forEach(function (action) {
-      try {
-        action();
-      } catch (e) {
-        // in case the page that registered the handler has been unloaded
-      }
-    });
-    this.connectActions = [];
-  }
-  executeDisconnectActions(message) {
-    this.disconnectActions.forEach(action => {
-      try {
-        action(message);
-      } catch (e) {
-        // in case the page that registered the handler has been unloaded
-      }
-    });
-    this.disconnectActions = [];
-  }
-  on(eventType, fn) {
-    if (eventType === "connected") {
-      this.addConnectAction(fn);
-    } else if (eventType === "disconnected") {
-      this.addDisconnectAction(fn);
-    } else {
-      console.log("unknown event type " + eventType);
-    }
-  }
-  setSchema(schema) {
-    this.schema = schema;
-  }
-  is_connected() {
-    return (
-      this.connection &&
-      this.sender &&
-      this.receiver &&
-      this.receiver.remote &&
-      this.receiver.remote.attach &&
-      this.receiver.remote.attach.source &&
-      this.receiver.remote.attach.source.address &&
-      this.connection.is_open()
-    );
-  }
-  disconnect() {
-    if (this.sender) this.sender.close();
-    if (this.receiver) this.receiver.close();
-    if (this.connection) this.connection.close();
-  }
-  createSenderReceiver(options) {
-    return new Promise(
-      function (resolve, reject) {
-        var timeout = options.timeout || 10000;
-        // set a timer in case the setup takes too long
-        var giveUp = function () {
-          this.connection.removeListener("receiver_open", receiver_open);
-          this.connection.removeListener("sendable", sendable);
-          this.errorText = "timed out creating senders and receivers";
-          reject(Error(this.errorText));
-        }.bind(this);
-        var timer = setTimeout(giveUp, timeout);
-        // register an event hander for when the setup is complete
-        var sendable = function (context) {
-          clearTimeout(timer);
-          this.version = this.connection.properties
-            ? this.connection.properties.version
-            : "0.1.0";
-          // in case this connection dies
-          rhea.on("disconnected", this.on_disconnected);
-          // in case this connection dies and is then reconnected automatically
-          rhea.on("connection_open", this.on_connection_open);
-          // receive messages here
-          this.connection.on("message", this.on_message);
-          resolve(context);
-        }.bind(this);
-        this.connection.once("sendable", sendable);
-        // Now actually createt the sender and receiver.
-        // register an event handler for when the receiver opens
-        var receiver_open = function () {
-          // once the receiver is open, create the sender
-          if (options.sender_address)
-            this.sender = this.connection.open_sender(options.sender_address);
-          else this.sender = this.connection.open_sender();
-        }.bind(this);
-        this.connection.once("receiver_open", receiver_open);
-        // create a dynamic receiver
-        this.receiver = this.connection.open_receiver({
-          source: { dynamic: true }
-        });
-      }.bind(this)
-    );
-  }
-  connect(options) {
-    return new Promise(
-      function (resolve, reject) {
-        var finishConnecting = function () {
-          this.createSenderReceiver(options).then(
-            function (results) {
-              resolve(results);
-            },
-            function (error) {
-              reject(error);
-            }
-          );
-        };
-        if (!this.connection) {
-          options.test = false; // if you didn't want a connection, you should have called testConnect() and not connect()
-          this.testConnect(options).then(
-            function () {
-              finishConnecting.call(this);
-            }.bind(this),
-            function () {
-              // connect failed or timed out
-              this.errorText = `Unable to connect to ${options.address}:${options.port}`;
-              this.executeDisconnectActions(this.errorText);
-              reject(Error(this.errorText));
-            }.bind(this)
-          );
-        } else {
-          finishConnecting.call(this);
-        }
-      }.bind(this)
-    );
-  }
-  getReceiverAddress() {
-    return this.receiver.remote.attach.source.address;
-  }
-  // Try to connect using the options.
-  // if options.test === true -> close the connection if it succeeded and resolve the promise
-  // if the connection attempt fails or times out, reject the promise regardless of options.test
-  testConnect(options, callback) {
-    return new Promise(
-      function (resolve, reject) {
-        var timeout = options.timeout || 10000;
-        var reconnect = options.reconnect || false; // in case options.reconnect is undefined
-        var baseAddress = options.address + ":" + options.port;
-        if (options.linkRouteAddress) {
-          baseAddress += "/" + options.linkRouteAddress;
-        }
-        var wsprotocol = window.location.protocol === "https:" ? "wss" : "ws";
-        if (this.connection) {
-          delete this.connection;
-          this.connection = null;
-        }
-        var ws = rhea.websocket_connect(WebSocket);
-        var c = {
-          connection_details: new ws(wsprotocol + "://" + baseAddress, [
-            "binary"
-          ]),
-          reconnect: reconnect,
-          properties: options.properties || {
-            console_identifier: "Dispatch console"
-          }
-        };
-        if (options.hostname) c.hostname = options.hostname;
-        if (options.username && options.username !== "") {
-          c.username = options.username;
-        }
-        if (options.password && options.password !== "") {
-          c.password = options.password;
-        }
-        // set a timeout
-        var disconnected = function () {
-          clearTimeout(timer);
-          rhea.removeListener("disconnected", disconnected);
-          rhea.removeListener("connection_open", connection_open);
-          this.connection = null;
-          var rej = "failed to connect";
-          if (callback) callback({ error: rej });
-          reject(Error(rej));
-        }.bind(this);
-        var timer = setTimeout(disconnected, timeout);
-        // the event handler for when the connection opens
-        var connection_open = function (context) {
-          clearTimeout(timer);
-          // prevent future disconnects from calling reject
-          rhea.removeListener("disconnected", disconnected);
-          // we were just checking. we don't really want a connection
-          if (options.test) {
-            context.connection.close();
-            this.connection = null;
-          } else this.on_connection_open();
-          var res = { context: context };
-          if (callback) callback(res);
-          resolve(res);
-        }.bind(this);
-        // register an event handler for when the connection opens
-        rhea.once("connection_open", connection_open);
-        // register an event handler for if the connection fails to open
-        rhea.once("disconnected", disconnected);
-        // attempt the connection
-        this.connection = rhea.connect(c);
-      }.bind(this)
-    );
-  }
-  sendMgmtQuery(operation, to) {
-    to = to || "/$management";
-    return this.send([], to, operation);
-  }
-  sendQuery(toAddr, entity, attrs, operation) {
-    operation = operation || "QUERY";
-    var fullAddr = this._fullAddr(toAddr);
-    var body = { attributeNames: attrs || [] };
-    return this.send(
-      body,
-      fullAddr,
-      operation,
-      this.schema.entityTypes[entity].fullyQualifiedType
-    );
-  }
-  send(body, to, operation, entityType) {
-    var application_properties = {
-      operation: operation,
-      type: "org.amqp.management",
-      name: "self"
-    };
-    if (entityType) application_properties.entityType = entityType;
-    return this._send(body, to, application_properties);
-  }
-  sendMethod(toAddr, entity, attrs, operation, props) {
-    var fullAddr = this._fullAddr(toAddr);
-    var application_properties = {
-      operation: operation
-    };
-    if (entity) {
-      application_properties.type = this.schema.entityTypes[
-        entity
-      ].fullyQualifiedType;
-    }
-    if (attrs.name) application_properties.name = attrs.name;
-    else if (attrs.identity) application_properties.identity = attrs.identity;
-    if (props) {
-      for (var attrname in props) {
-        application_properties[attrname] = props[attrname];
-      }
-    }
-    return this._send(attrs, fullAddr, application_properties);
-  }
-  _send(body, to, application_properties) {
-    var _correlationId = this.correlator.corr();
-    var self = this;
-    return new Promise(function (resolve, reject) {
-      self.correlator.register(_correlationId, resolve, reject);
-      self.sender.send({
-        body: body,
-        to: to,
-        reply_to: self.receiver.remote.attach.source.address,
-        correlation_id: _correlationId,
-        application_properties: application_properties
-      });
-    });
-  }
-  _fullAddr(toAddr) {
-    var toAddrParts = toAddr.split("/");
-    toAddrParts.shift();
-    var fullAddr = toAddrParts.join("/");
-    return fullAddr;
-  }
-  availableQeueuDepth() {
-    return this.correlator.depth();
-  }
-}
-
-class ConnectionException {
-  constructor(message) {
-    this.message = message;
-    this.name = "ConnectionException";
-  }
-}
-
-const _ConnectionManager = ConnectionManager;
-export { _ConnectionManager as ConnectionManager };
-const _ConnectionException = ConnectionException;
-export { _ConnectionException as ConnectionException };
diff --git a/console/react/src/brokers.ttf b/console/react/src/brokers.ttf
deleted file mode 100644
index e69de29..0000000
diff --git a/console/react/src/chord/chordToolbar.js b/console/react/src/chord/chordToolbar.js
index 4f5fc16..d2a5226 100644
--- a/console/react/src/chord/chordToolbar.js
+++ b/console/react/src/chord/chordToolbar.js
@@ -1,16 +1,35 @@
+/*
+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.
+*/
+
 import React from "react";
 import { Toolbar, ToolbarGroup, ToolbarItem } from "@patternfly/react-core";
 import OptionsComponent from "./optionsComponent";
 import RoutersComponent from "./routersComponent";
-import DropdownPanel from "../dropdownPanel"
+import DropdownPanel from "../common/dropdownPanel";
 
 class ChordToolbar extends React.Component {
-
   render() {
     return (
       <Toolbar
         data-testid="chord-toolbar"
-        className="pf-l-toolbar pf-u-justify-content-space-between pf-u-mx-xl pf-u-my-md">
+        className="pf-l-toolbar pf-u-justify-content-space-between pf-u-mx-xl pf-u-my-md"
+      >
         <ToolbarGroup>
           <ToolbarItem className="pf-u-mr-md">
             <DropdownPanel
diff --git a/console/react/src/chord/chordToolbar.test.js b/console/react/src/chord/chordToolbar.test.js
index bb53359..793fa65 100644
--- a/console/react/src/chord/chordToolbar.test.js
+++ b/console/react/src/chord/chordToolbar.test.js
@@ -1,3 +1,22 @@
+/*
+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.
+*/
+
 import React from "react";
 import { render, fireEvent } from "@testing-library/react";
 import ChordToolbar from "./chordToolbar";
diff --git a/console/react/src/chord/chordViewer.js b/console/react/src/chord/chordViewer.js
index b2ad22e..0d2c177 100644
--- a/console/react/src/chord/chordViewer.js
+++ b/console/react/src/chord/chordViewer.js
@@ -25,7 +25,7 @@ import { ChordData } from "./data.js";
 import { qdrRibbon } from "./ribbon/ribbon.js";
 import { qdrlayoutChord } from "./layout/layout.js";
 import ChordToolbar from "./chordToolbar";
-import QDRPopup from "../qdrPopup";
+import QDRPopup from "../common/qdrPopup";
 import * as d3 from "d3";
 
 const CHORDOPTIONSKEY = "chordOptions";
diff --git a/console/react/src/chord/chordViewer.test.js b/console/react/src/chord/chordViewer.test.js
index 76e9265..2de8fa0 100644
--- a/console/react/src/chord/chordViewer.test.js
+++ b/console/react/src/chord/chordViewer.test.js
@@ -1,13 +1,39 @@
+/*
+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.
+*/
+
 import React from "react";
 import { render, fireEvent, waitForElement } from "@testing-library/react";
-import { mockService } from "../qdrService.mock";
-import ChordViewer from "./chordViewer";
+import { service, login, TEST_PORT } from "../serviceTest";
 import { LocalStorageMock } from "@react-mock/localstorage";
+import ChordViewer from "./chordViewer";
 
 it("renders the ChordViewer component", async () => {
   const props = {
-    service: mockService({})
+    service
   };
+
+  if (!TEST_PORT) {
+    console.log("using mock service");
+  }
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
+
   const { getByLabelText } = render(
     <LocalStorageMock
       items={{
diff --git a/console/react/src/chord/legendComponent.js b/console/react/src/chord/legendComponent.js
index 9ad75ac..0736984 100644
--- a/console/react/src/chord/legendComponent.js
+++ b/console/react/src/chord/legendComponent.js
@@ -26,7 +26,7 @@ import {
 } from "@patternfly/react-core";
 import OptionsComponent from "./optionsComponent";
 import RoutersComponent from "./routersComponent";
-import AddressesComponent from "../addressesComponent";
+import AddressesComponent from "../common/addressesComponent";
 
 class LegendComponent extends Component {
   constructor(props) {
diff --git a/console/react/src/chord/optionsComponent.js b/console/react/src/chord/optionsComponent.js
index c7d920c..4e674dc 100644
--- a/console/react/src/chord/optionsComponent.js
+++ b/console/react/src/chord/optionsComponent.js
@@ -19,7 +19,7 @@ under the License.
 
 import React, { Component } from "react";
 import { Checkbox } from "@patternfly/react-core";
-import AddressesComponent from "../addressesComponent";
+import AddressesComponent from "../common/addressesComponent";
 
 class OptionsComponent extends Component {
   constructor(props) {
diff --git a/console/react/src/DropdownMenu.js b/console/react/src/common/DropdownMenu.js
similarity index 94%
rename from console/react/src/DropdownMenu.js
rename to console/react/src/common/DropdownMenu.js
index c51df43..30b0db7 100644
--- a/console/react/src/DropdownMenu.js
+++ b/console/react/src/common/DropdownMenu.js
@@ -25,9 +25,7 @@ class DropdownMenu extends Component {
     super(props);
     this.state = {
       isVisible:
-        typeof this.props.isVisible !== "undefined"
-          ? this.props.isVisible
-          : false
+        typeof this.props.isVisible !== "undefined" ? this.props.isVisible : false
     };
     this.contextMenuItems = [
       {
diff --git a/console/react/src/details/dataSources/addressData.js b/console/react/src/common/DropdownMenu.test.js
similarity index 56%
copy from console/react/src/details/dataSources/addressData.js
copy to console/react/src/common/DropdownMenu.test.js
index 13c7e97..4442441 100644
--- a/console/react/src/details/dataSources/addressData.js
+++ b/console/react/src/common/DropdownMenu.test.js
@@ -18,28 +18,24 @@ under the License.
 */
 
 import React from "react";
-import DefaultData from "./defaultData";
-import { utils } from "../../amqp/utilities";
+import { render } from "@testing-library/react";
+import DropdownMenu from "./DropdownMenu";
 
-const AddressType = ({ value, extraInfo }) => {
-  const data = extraInfo.rowData.data;
-  const identity = utils.identity_clean(data.identity);
-  const cls = utils.addr_class(identity);
-
-  return (
-    <span className="entity-type">
-      <i className={`address-${cls}`}></i>
-      {cls}
-    </span>
+it("the dropdown menu component renders and calls event handlers", () => {
+  const isVisible = true;
+  const isConnected = () => true;
+  let logoutCalled = false;
+  const handleDropdownLogout = () => (logoutCalled = true);
+  let menuRef = null;
+  render(
+    <DropdownMenu
+      ref={el => (menuRef = el)}
+      isVisible={isVisible}
+      isConnected={isConnected}
+      handleDropdownLogout={handleDropdownLogout}
+    />
   );
-};
-
-class AddressData extends DefaultData {
-  constructor(service, schema) {
-    super(service, schema);
-    this.typeFormatter = AddressType;
-    this.detailName = "router.address";
-  }
-}
-
-export default AddressData;
+  menuRef.show(true);
+  menuRef.logout();
+  expect(logoutCalled).toBe(true);
+});
diff --git a/console/react/src/addressesComponent.js b/console/react/src/common/addressesComponent.js
similarity index 100%
rename from console/react/src/addressesComponent.js
rename to console/react/src/common/addressesComponent.js
diff --git a/console/react/src/common/addressesComponent.test.js b/console/react/src/common/addressesComponent.test.js
new file mode 100644
index 0000000..291b861
--- /dev/null
+++ b/console/react/src/common/addressesComponent.test.js
@@ -0,0 +1,50 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render, fireEvent } from "@testing-library/react";
+import AddressesComponent from "./addressesComponent";
+
+it("renders the addresses component with an address", () => {
+  let handleChangeAddressCalled = false;
+  let handleHoverAddress = undefined;
+  const props = {
+    addresses: { test: true },
+    addressColors: { test: "#EAEAEA" },
+    handleChangeAddress: () => (handleChangeAddressCalled = true),
+    handleHoverAddress: (address, over) => (handleHoverAddress = over)
+  };
+  const { getByLabelText } = render(<AddressesComponent {...props} />);
+  const node = getByLabelText("colored dot");
+  fireEvent.click(node);
+  expect(handleChangeAddressCalled).toBe(true);
+  fireEvent.mouseOver(node);
+  expect(handleHoverAddress).toBe(true);
+  fireEvent.mouseOut(node);
+  expect(handleHoverAddress).toBe(false);
+});
+
+it("renders the addresses component without an address", () => {
+  const props = {
+    addresses: {},
+    addressColors: {}
+  };
+  const { getByText } = render(<AddressesComponent {...props} />);
+  expect(getByText("There is no traffic")).toBeInTheDocument();
+});
diff --git a/console/react/src/common/amqp/connection.js b/console/react/src/common/amqp/connection.js
new file mode 100644
index 0000000..bd8e3c0
--- /dev/null
+++ b/console/react/src/common/amqp/connection.js
@@ -0,0 +1,385 @@
+/*
+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.
+*/
+
+import Correlator from "./correlator";
+import rhea from "rhea";
+
+class ConnectionManager {
+  constructor(protocol) {
+    this.rhea = rhea;
+    this.sender = undefined;
+    this.receiver = undefined;
+    this.connection = undefined;
+    this.version = undefined;
+    this.errorText = undefined;
+    this.protocol = protocol;
+    this.schema = undefined;
+    this.connectActions = [];
+    this.disconnectActions = [];
+    this.correlator = new Correlator();
+    this.on_message = context => {
+      this.correlator.resolve(context);
+    };
+    this.on_disconnected = () => {
+      this.errorText = "Disconnected";
+      this.executeDisconnectActions(this.errorText);
+    };
+    this.on_connection_open = () => {
+      this.executeConnectActions();
+    };
+  }
+  versionCheck = minVer => {
+    var verparts = this.version.split(".");
+    var minparts = minVer.split(".");
+    try {
+      for (var i = 0; i < minparts.length; ++i) {
+        if (parseInt(minVer[i] > parseInt(verparts[i]))) return false;
+      }
+    } catch (e) {
+      return false;
+    }
+    return true;
+  };
+  addConnectAction = action => {
+    if (typeof action === "function") {
+      this.delConnectAction(action);
+      this.connectActions.push(action);
+    }
+  };
+  addDisconnectAction = action => {
+    if (typeof action === "function") {
+      this.delDisconnectAction(action);
+      this.disconnectActions.push(action);
+    }
+  };
+  delConnectAction = action => {
+    if (typeof action === "function") {
+      var index = this.connectActions.indexOf(action);
+      if (index >= 0) this.connectActions.splice(index, 1);
+    }
+  };
+  delDisconnectAction = action => {
+    if (typeof action === "function") {
+      var index = this.disconnectActions.indexOf(action);
+      if (index >= 0) this.disconnectActions.splice(index, 1);
+    }
+  };
+  executeConnectActions = () => {
+    this.connectActions.forEach(action => {
+      try {
+        action();
+      } catch (e) {
+        // in case the page that registered the handler has been unloaded
+      }
+    });
+    this.connectActions = [];
+  };
+  executeDisconnectActions = message => {
+    this.disconnectActions.forEach(action => {
+      try {
+        action(message);
+      } catch (e) {
+        // in case the page that registered the handler has been unloaded
+      }
+    });
+    this.disconnectActions = [];
+  };
+  on = (eventType, fn) => {
+    if (eventType === "connected") {
+      this.addConnectAction(fn);
+    } else if (eventType === "disconnected") {
+      this.addDisconnectAction(fn);
+    } else {
+      console.log("unknown event type " + eventType);
+    }
+  };
+  setSchema = schema => {
+    this.schema = schema;
+  };
+  is_connected = () => {
+    return (
+      this.connection &&
+      this.sender &&
+      this.receiver &&
+      this.receiver.remote &&
+      this.receiver.remote.attach &&
+      this.receiver.remote.attach.source &&
+      this.receiver.remote.attach.source.address &&
+      this.connection.is_open()
+    );
+  };
+  disconnect = () => {
+    if (this.sender) this.sender.close();
+    if (this.receiver) this.receiver.close();
+    if (this.connection) {
+      this.connection.close();
+      this.connection = null;
+    }
+  };
+  restrict = (count, f) => {
+    if (count) {
+      var current = count;
+      var reset;
+      return successful_attempts => {
+        if (reset !== successful_attempts) {
+          current = count;
+          reset = successful_attempts;
+        }
+        if (current--) return f(successful_attempts);
+        else return -1;
+      };
+    } else {
+      return f;
+    }
+  };
+
+  backoff = (initial, max) => {
+    var delay = initial;
+    var reset;
+    return successful_attempts => {
+      if (reset !== successful_attempts) {
+        delay = initial;
+        reset = successful_attempts;
+      }
+      var current = delay;
+      var next = delay * 2;
+      delay = max > next ? next : max;
+      return current;
+    };
+  };
+
+  setReconnect = reconnect => {
+    if (this.connection) {
+      if (reconnect) {
+        var initial = this.connection.get_option("initial_reconnect_delay", 100);
+        var max = this.connection.get_option("max_reconnect_delay", 60000);
+        this.connection.options.reconnect = this.restrict(
+          this.connection.get_option("reconnect_limit"),
+          this.backoff(initial, max)
+        );
+      } else {
+        this.connection.options.reconnect = false;
+      }
+    }
+  };
+
+  createSenderReceiver = options => {
+    return new Promise((resolve, reject) => {
+      var timeout = options.timeout || 10000;
+      // set a timer in case the setup takes too long
+      var giveUp = () => {
+        this.connection.removeListener("receiver_open", receiver_open);
+        this.connection.removeListener("sendable", sendable);
+        this.errorText = "timed out creating senders and receivers";
+        reject(Error(this.errorText));
+      };
+      var timer = setTimeout(giveUp, timeout);
+      // register an event hander for when the setup is complete
+      var sendable = context => {
+        clearTimeout(timer);
+        this.version = this.connection.properties
+          ? this.connection.properties.version
+          : "0.1.0";
+        // in case this connection dies
+        this.rhea.on("disconnected", this.on_disconnected);
+        // in case this connection dies and is then reconnected automatically
+        this.rhea.on("connection_open", this.on_connection_open);
+        // receive messages here
+        this.connection.on("message", this.on_message);
+        resolve(context);
+      };
+      this.connection.once("sendable", sendable);
+      // Now actually createt the sender and receiver.
+      // register an event handler for when the receiver opens
+      var receiver_open = () => {
+        // once the receiver is open, create the sender
+        if (options.sender_address)
+          this.sender = this.connection.open_sender(options.sender_address);
+        else this.sender = this.connection.open_sender();
+      };
+      this.connection.once("receiver_open", receiver_open);
+      // create a dynamic receiver
+      this.receiver = this.connection.open_receiver({
+        source: { dynamic: true }
+      });
+    });
+  };
+
+  connect = options => {
+    return new Promise((resolve, reject) => {
+      var finishConnecting = () => {
+        this.createSenderReceiver(options).then(
+          results => {
+            resolve(results);
+          },
+          error => {
+            reject(error);
+          }
+        );
+      };
+      if (!this.is_connected()) {
+        this.doConnect(options).then(
+          () => {
+            finishConnecting.call(this);
+          },
+          () => {
+            // connect failed or timed out
+            this.disconnect();
+            this.errorText = `Unable to connect to ${options.address}:${options.port}`;
+            this.executeDisconnectActions(this.errorText);
+            reject(Error(this.errorText));
+          }
+        );
+      } else {
+        console.log("called connect when already connected");
+        finishConnecting.call(this);
+      }
+    });
+  };
+  getReceiverAddress = () => {
+    return this.receiver.remote.attach.source.address;
+  };
+
+  doConnect = options => {
+    return new Promise((resolve, reject) => {
+      var timeout = options.timeout || 10000;
+      var reconnect = options.reconnect || false; // in case options.reconnect is undefined
+      var baseAddress = options.address + ":" + options.port;
+      if (options.linkRouteAddress) {
+        baseAddress += "/" + options.linkRouteAddress;
+      }
+      var wsprotocol = window.location.protocol === "https:" ? "wss" : "ws";
+      var ws = this.rhea.websocket_connect(WebSocket);
+      var c = {
+        connection_details: new ws(wsprotocol + "://" + baseAddress, ["binary"]),
+        reconnect: reconnect,
+        properties: options.properties || {
+          console_identifier: "Dispatch console"
+        }
+      };
+      if (options.hostname) c.hostname = options.hostname;
+      if (options.username && options.username !== "") {
+        c.username = options.username;
+      }
+      if (options.password && options.password !== "") {
+        c.password = options.password;
+      }
+      // set a timeout
+      var timedOut = () => {
+        clearTimeout(timer);
+        this.rhea.removeListener("disconnected", timedOut);
+        this.rhea.removeListener("connection_open", connection_open);
+        //this.connection = null;
+        var rej = "failed to connect";
+        reject(Error(rej));
+      };
+      var timer = setTimeout(timedOut, timeout);
+      // the event handler for when the connection opens
+      var connection_open = context => {
+        clearTimeout(timer);
+        // prevent future disconnects from calling reject
+        this.rhea.removeListener("disconnected", timedOut);
+        this.on_connection_open();
+        resolve({ context: context });
+      };
+      // register an event handler for when the connection opens
+      this.rhea.once("connection_open", connection_open);
+      // register an event handler for if the connection fails to open
+      this.rhea.once("disconnected", timedOut);
+      // attempt the connection
+      this.connection = this.rhea.connect(c);
+    });
+  };
+  sendMgmtQuery = (operation, to) => {
+    to = to || "/$management";
+    return this.send([], to, operation);
+  };
+  sendQuery = (toAddr, entity, attrs, operation) => {
+    operation = operation || "QUERY";
+    var fullAddr = this._fullAddr(toAddr);
+    var body = { attributeNames: attrs || [] };
+    return this.send(
+      body,
+      fullAddr,
+      operation,
+      this.schema.entityTypes[entity].fullyQualifiedType
+    );
+  };
+  send = (body, to, operation, entityType) => {
+    var application_properties = {
+      operation: operation,
+      type: "org.amqp.management",
+      name: "self"
+    };
+    if (entityType) application_properties.entityType = entityType;
+    return this._send(body, to, application_properties);
+  };
+  sendMethod = (toAddr, entity, attrs, operation, props) => {
+    var fullAddr = this._fullAddr(toAddr);
+    var application_properties = {
+      operation: operation
+    };
+    if (entity) {
+      application_properties.type = this.schema.entityTypes[entity].fullyQualifiedType;
+    }
+    if (attrs.name) application_properties.name = attrs.name;
+    else if (attrs.identity) application_properties.identity = attrs.identity;
+    if (props) {
+      for (var attrname in props) {
+        application_properties[attrname] = props[attrname];
+      }
+    }
+    return this._send(attrs, fullAddr, application_properties);
+  };
+  _send = (body, to, application_properties) => {
+    var _correlationId = this.correlator.corr();
+    var self = this;
+    return new Promise((resolve, reject) => {
+      self.correlator.register(_correlationId, resolve, reject);
+      self.sender.send({
+        body: body,
+        to: to,
+        reply_to: self.receiver.remote.attach.source.address,
+        correlation_id: _correlationId,
+        application_properties: application_properties
+      });
+    });
+  };
+  _fullAddr = toAddr => {
+    var toAddrParts = toAddr.split("/");
+    toAddrParts.shift();
+    var fullAddr = toAddrParts.join("/");
+    return fullAddr;
+  };
+  availableQeueuDepth = () => {
+    return this.correlator.depth();
+  };
+}
+
+class ConnectionException {
+  constructor(message) {
+    this.message = message;
+    this.name = "ConnectionException";
+  }
+}
+
+const _ConnectionManager = ConnectionManager;
+export { _ConnectionManager as ConnectionManager };
+const _ConnectionException = ConnectionException;
+export { _ConnectionException as ConnectionException };
diff --git a/console/react/src/amqp/correlator.js b/console/react/src/common/amqp/correlator.js
similarity index 51%
rename from console/react/src/amqp/correlator.js
rename to console/react/src/common/amqp/correlator.js
index bf34f93..c9f3e99 100644
--- a/console/react/src/amqp/correlator.js
+++ b/console/react/src/common/amqp/correlator.js
@@ -1,20 +1,23 @@
 /*
-  * Copyright 2017 Red Hat Inc.
-  *
-  * Licensed 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.
-  */
+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
 
-import { utils } from './utilities.js';
+  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.
+*/
+
+import { utils } from "./utilities";
 
 class Correlator {
   constructor() {
@@ -23,7 +26,7 @@ class Correlator {
     this.maxCorrelatorDepth = 10;
   }
   corr() {
-    return ++(this._correlationID) + '';
+    return ++this._correlationID + "";
   }
   // Associate this correlation id with the promise's resolve and reject methods
   register(id, resolve, reject) {
@@ -34,7 +37,10 @@ class Correlator {
   resolve(context) {
     var correlationID = context.message.correlation_id;
     // call the promise's resolve function with a copy of the rhea response (so we don't keep any references to internal rhea data)
-    this._objects[correlationID].resolver({ response: utils.copy(context.message.body), context: context });
+    this._objects[correlationID].resolver({
+      response: utils.copy(context.message.body),
+      context: context
+    });
     delete this._objects[correlationID];
   }
   reject(id, error) {
diff --git a/console/react/src/amqp/management.js b/console/react/src/common/amqp/management.js
similarity index 67%
rename from console/react/src/amqp/management.js
rename to console/react/src/common/amqp/management.js
index 19ca359..6711da6 100644
--- a/console/react/src/amqp/management.js
+++ b/console/react/src/common/amqp/management.js
@@ -1,20 +1,21 @@
 /*
- * Copyright 2015 Red Hat Inc.
- *
- * Licensed 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.
- */
+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
 
-/* global Promise */
+  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.
+*/
 
 import { ConnectionManager } from "./connection.js";
 import Topology from "./topology.js";
@@ -45,9 +46,7 @@ export class Management {
                 var attribute = entity.attributes[attributeName];
                 if (attribute.deprecated) {
                   // deprecated attribute
-                  delete response.entityTypes[entityName].attributes[
-                    attributeName
-                  ];
+                  delete response.entityTypes[entityName].attributes[attributeName];
                 }
               }
             }
diff --git a/console/react/src/amqp/topology.js b/console/react/src/common/amqp/topology.js
similarity index 85%
rename from console/react/src/amqp/topology.js
rename to console/react/src/common/amqp/topology.js
index 98b03da..c0ca63e 100644
--- a/console/react/src/amqp/topology.js
+++ b/console/react/src/common/amqp/topology.js
@@ -1,18 +1,21 @@
 /*
- * Copyright 2015 Red Hat Inc.
- *
- * Licensed 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.
- */
+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.
+*/
 
 import { utils } from "./utilities.js";
 
@@ -103,9 +106,7 @@ class Topology {
             // check for changed number of connections
             if (entity === "connection") {
               const oldConnections =
-                this._nodeInfo &&
-                this._nodeInfo[rId] &&
-                this._nodeInfo[rId].connection
+                this._nodeInfo && this._nodeInfo[rId] && this._nodeInfo[rId].connection
                   ? this._nodeInfo[rId].connection.results.length
                   : 0;
               const newConnections = workInfo[rId].connection.results.length;
@@ -146,9 +147,7 @@ class Topology {
         this.connection.sendMgmtQuery("GET-MGMT-NODES").then(
           function(results) {
             let routerIds = results.response;
-            if (
-              Object.prototype.toString.call(routerIds) === "[object Array]"
-            ) {
+            if (Object.prototype.toString.call(routerIds) === "[object Array]") {
               // if there is only one node, it will not be returned
               if (routerIds.length === 0) {
                 var parts = this.connection.getReceiverAddress().split("/");
@@ -190,29 +189,26 @@ class Topology {
                   // test for edge case
                   let routerId = connectedToEdge(routerIds, workInfo);
                   if (routerId) {
-                    this.connection
-                      .sendMgmtQuery("GET-MGMT-NODES", routerId)
-                      .then(
-                        function(results) {
-                          let response = results.response;
-                          if (
-                            Object.prototype.toString.call(response) ===
-                            "[object Array]"
-                          ) {
-                            // special case of edge case:
-                            // we are connected to an edge router that is connected to
-                            // a router that is not connected to any other interior routers
-                            if (response.length === 0) {
-                              response = [routerId];
-                            }
-                            this.doget(response).then(
-                              function(workInfo) {
-                                finish.call(this, workInfo);
-                              }.bind(this)
-                            );
+                    this.connection.sendMgmtQuery("GET-MGMT-NODES", routerId).then(
+                      function(results) {
+                        let response = results.response;
+                        if (
+                          Object.prototype.toString.call(response) === "[object Array]"
+                        ) {
+                          // special case of edge case:
+                          // we are connected to an edge router that is connected to
+                          // a router that is not connected to any other interior routers
+                          if (response.length === 0) {
+                            response = [routerId];
                           }
-                        }.bind(this)
-                      );
+                          this.doget(response).then(
+                            function(workInfo) {
+                              finish.call(this, workInfo);
+                            }.bind(this)
+                          );
+                        }
+                      }.bind(this)
+                    );
                   } else {
                     finish.call(this, workInfo);
                   }
@@ -290,25 +286,14 @@ class Topology {
       results = response;
     };
     var q = queue(this.connection.availableQeueuDepth());
-    q.defer(
-      this.q_fetchNodeInfo.bind(this),
-      node,
-      entity,
-      attrs,
-      q,
-      gotResponse
-    );
+    q.defer(this.q_fetchNodeInfo.bind(this), node, entity, attrs, q, gotResponse);
     q.await(function() {
       callback(node, entity, results);
     });
   }
   // called from queue.defer so the last argument (callback) is supplied by d3
   q_fetchNodeInfo(nodeId, entity, attrs, q, heartbeat, callback) {
-    this.getNodeInfo(nodeId, entity, attrs, q, function(
-      nodeName,
-      dotentity,
-      response
-    ) {
+    this.getNodeInfo(nodeId, entity, attrs, q, function(nodeName, dotentity, response) {
       heartbeat(nodeName, dotentity, response);
       callback(null);
     });
@@ -380,12 +365,7 @@ class Topology {
   }
   // enusre all the topology nones have all these entities
   ensureAllEntities(entityAttribs, callback, extra) {
-    this.ensureEntities(
-      Object.keys(this._nodeInfo),
-      entityAttribs,
-      callback,
-      extra
-    );
+    this.ensureEntities(Object.keys(this._nodeInfo), entityAttribs, callback, extra);
   }
   // ensure these nodes have all these entities. don't fetch unless forced to
   ensureEntities(nodes, entityAttribs, callback, extra) {
@@ -508,9 +488,7 @@ class Topology {
           response.response.results.length === 0 &&
           Object.keys(self._nodeInfo).length === 1
         ) {
-          response.response.results = [
-            self.getSingelRouterNode(nodeName, attrs)
-          ];
+          response.response.results = [self.getSingelRouterNode(nodeName, attrs)];
         }
         callback(nodeName, entity, response.response);
       },
@@ -519,14 +497,7 @@ class Topology {
       }
     );
   }
-  getMultipleNodeInfo(
-    nodeNames,
-    entity,
-    attrs,
-    callback,
-    selectedNodeId,
-    aggregate
-  ) {
+  getMultipleNodeInfo(nodeNames, entity, attrs, callback, selectedNodeId, aggregate) {
     var self = this;
     if (typeof aggregate === "undefined") aggregate = true;
     var responses = {};
@@ -535,24 +506,11 @@ class Topology {
     };
     var q = queue(this.connection.availableQeueuDepth());
     nodeNames.forEach(function(id) {
-      q.defer(
-        self.q_fetchNodeInfo.bind(self),
-        id,
-        entity,
-        attrs,
-        q,
-        gotNodesResult
-      );
+      q.defer(self.q_fetchNodeInfo.bind(self), id, entity, attrs, q, gotNodesResult);
     });
     q.await(function() {
       if (aggregate)
-        self.aggregateNodeInfo(
-          nodeNames,
-          entity,
-          selectedNodeId,
-          responses,
-          callback
-        );
+        self.aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback);
       else {
         callback(nodeNames, entity, responses);
       }
@@ -563,12 +521,7 @@ class Topology {
       adminStatus: "disabled",
       name: name
     };
-    return this.connection.sendMethod(
-      nodeId,
-      "router.link",
-      attributes,
-      "UPDATE"
-    );
+    return this.connection.sendMethod(nodeId, "router.link", attributes, "UPDATE");
   }
   aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback) {
     // aggregate the responses
diff --git a/console/react/src/amqp/utilities.js b/console/react/src/common/amqp/utilities.js
similarity index 86%
rename from console/react/src/amqp/utilities.js
rename to console/react/src/common/amqp/utilities.js
index 30e4ec8..61073c2 100644
--- a/console/react/src/amqp/utilities.js
+++ b/console/react/src/common/amqp/utilities.js
@@ -1,18 +1,21 @@
 /*
- * Copyright 2018 Red Hat Inc.
- *
- * Licensed 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.
- */
+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.
+*/
 
 import * as d3 from "d3";
 
@@ -26,23 +29,19 @@ var utils = {
     });
   },
   isConsole: function(d) {
-    return (
-      d &&
-      d.properties &&
-      d.properties.console_identifier === "Dispatch console"
-    );
+    return d && d.properties && d.properties.console_identifier === "Dispatch console";
   },
   isArtemis: function(d) {
     return (
       (d.nodeType === "route-container" || d.nodeType === "on-demand") &&
-      (d.properties && d.properties.product === "apache-activemq-artemis")
+      d.properties && d.properties.product === "apache-activemq-artemis"
     );
   },
 
   isQpid: function(d) {
     return (
       (d.nodeType === "route-container" || d.nodeType === "on-demand") &&
-      (d.properties && d.properties.product === "qpid-cpp")
+      d.properties && d.properties.product === "qpid-cpp"
     );
   },
 
@@ -51,8 +50,7 @@ var utils = {
     if (d.container) name = d.container;
     if (d.properties) {
       if (d.properties.product) name = d.properties.product;
-      else if (d.properties.console_identifier)
-        name = d.properties.console_identifier;
+      else if (d.properties.console_identifier) name = d.properties.console_identifier;
       else if (d.properties.name) name = d.properties.name;
     }
     return name;
@@ -223,10 +221,7 @@ var utils = {
   },
   uuidv4: function() {
     return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
-      (
-        c ^
-        (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
-      ).toString(16)
+      (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
     );
   },
   getUrlParts: function(fullUrl) {
diff --git a/console/react/src/connectionClose.js b/console/react/src/common/connectionClose.js
similarity index 100%
rename from console/react/src/connectionClose.js
rename to console/react/src/common/connectionClose.js
diff --git a/console/react/src/common/connectionClose.test.js b/console/react/src/common/connectionClose.test.js
new file mode 100644
index 0000000..e381932
--- /dev/null
+++ b/console/react/src/common/connectionClose.test.js
@@ -0,0 +1,58 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render, fireEvent } from "@testing-library/react";
+import ConnectionClose from "./connectionClose";
+import { mockService } from "../../test_data/qdrService.mock";
+
+it("renders the ConnectionClose component", () => {
+  let sendMethodCalled = false;
+  let handleAddNotificationCalled = false;
+  let notifyClickCalled = false;
+
+  const props = {
+    service: mockService({ onSendMethod: () => (sendMethodCalled = true) }),
+    handleAddNotification: () => (handleAddNotificationCalled = true),
+    notifyClick: () => (notifyClickCalled = true),
+    extraInfo: { rowData: { data: { name: "test record", role: "normal" } } }
+  };
+  const { getByLabelText, queryByLabelText } = render(<ConnectionClose {...props} />);
+
+  // the close button should be there
+  const closeButton = getByLabelText("connection-close-button");
+  expect(closeButton).toBeInTheDocument();
+
+  // the confirmation dialog should not be there
+  expect(queryByLabelText("connection-close-modal")).toBeNull();
+
+  // clicking the close button should display the confirmation dialog
+  fireEvent.click(closeButton);
+  expect(getByLabelText("connection-close-modal")).toBeInTheDocument();
+
+  const confirmButton = getByLabelText("connection-close-confirm");
+  expect(confirmButton).toBeInTheDocument();
+  fireEvent.click(confirmButton);
+
+  expect(sendMethodCalled).toBe(true);
+  setTimeout(() => {
+    expect(handleAddNotificationCalled).toBe(true);
+    expect(notifyClickCalled).toBe(true);
+  }, 1);
+});
diff --git a/console/react/src/details/dataSources/connectionData.js b/console/react/src/common/contextMenu.test.js
similarity index 51%
copy from console/react/src/details/dataSources/connectionData.js
copy to console/react/src/common/contextMenu.test.js
index 2271434..3fabf0c 100644
--- a/console/react/src/details/dataSources/connectionData.js
+++ b/console/react/src/common/contextMenu.test.js
@@ -18,33 +18,23 @@ under the License.
 */
 
 import React from "react";
-import DefaultData from "./defaultData";
-import ConnectionClose from "../../connectionClose";
+import { render, fireEvent } from "@testing-library/react";
+import ContextMenu from "./contextMenuComponent";
 
-class ConnectionData extends DefaultData {
-  constructor(service, schema) {
-    super(service, schema);
-    this.extraFields = [
-      {
-        title: "",
-        field: "connection",
-        noSort: true,
-        formatter: ConnectionClose
-      }
-    ];
-    this.detailEntity = "router.link";
-    this.detailName = "Link";
-  }
-
-  detailActions = (entity, props, record) => {
-    return (
-      <ConnectionClose
-        asButton={true}
-        extraInfo={{ rowData: { data: record } }}
-        {...props}
-      />
-    );
+it("the contextMenu component renders and calls event handlers", () => {
+  let handleContextHideClicked = false;
+  let itemActionCalled = false;
+  const props = {
+    handleContextHide: () => (handleContextHideClicked = true),
+    menuItems: [{ enabled: () => true, action: () => (itemActionCalled = true) }],
+    contextEventData: {},
+    contextEventPosition: [-1, -1]
   };
-}
+  const { getByLabelText } = render(<ContextMenu {...props} />);
+  const menuItem = getByLabelText("context-menu-item");
+  expect(menuItem).toBeInTheDocument();
 
-export default ConnectionData;
+  fireEvent.click(menuItem);
+  expect(handleContextHideClicked).toBe(true);
+  expect(itemActionCalled).toBe(true);
+});
diff --git a/console/react/src/contextMenuComponent.js b/console/react/src/common/contextMenuComponent.js
similarity index 57%
rename from console/react/src/contextMenuComponent.js
rename to console/react/src/common/contextMenuComponent.js
index d85516c..b021ff0 100644
--- a/console/react/src/contextMenuComponent.js
+++ b/console/react/src/common/contextMenuComponent.js
@@ -1,3 +1,22 @@
+/*
+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.
+*/
+
 import React from "react";
 
 class ContextMenuComponent extends React.Component {
@@ -31,10 +50,8 @@ class ContextMenuComponent extends React.Component {
   render() {
     const menuItems = this.props.menuItems.map((item, i) => {
       let className = `menu-item${
-        item.endGroup || i === this.props.menuItems.length - 1
-          ? " separator"
-          : ""
-        } ${item.enabled(this.props.contextEventData) ? "" : " disabled"}`;
+        item.endGroup || i === this.props.menuItems.length - 1 ? " separator" : ""
+      } ${item.enabled(this.props.contextEventData) ? "" : " disabled"}`;
 
       return (
         <li
@@ -53,9 +70,9 @@ class ContextMenuComponent extends React.Component {
     const style =
       this.props.contextEventPosition[0] >= 0
         ? {
-          left: `${this.props.contextEventPosition[0]}px`,
-          top: `${this.props.contextEventPosition[1]}px`
-        }
+            left: `${this.props.contextEventPosition[0]}px`,
+            top: `${this.props.contextEventPosition[1]}px`
+          }
         : {};
     return (
       <ul
diff --git a/console/react/src/dropdownPanel.js b/console/react/src/common/dropdownPanel.js
similarity index 51%
rename from console/react/src/dropdownPanel.js
rename to console/react/src/common/dropdownPanel.js
index f2ab1ff..c34ca1c 100644
--- a/console/react/src/dropdownPanel.js
+++ b/console/react/src/common/dropdownPanel.js
@@ -1,5 +1,29 @@
-import React from 'react';
-import { Accordion, AccordionItem, AccordionContent, AccordionToggle } from '@patternfly/react-core';
+/*
+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.
+*/
+
+import React from "react";
+import {
+  Accordion,
+  AccordionItem,
+  AccordionContent,
+  AccordionToggle
+} from "@patternfly/react-core";
 
 class DropdownPanel extends React.Component {
   constructor(props) {
@@ -25,24 +49,20 @@ class DropdownPanel extends React.Component {
 
   close = () => {
     this.setState({ expanded: false });
-  }
+  };
 
   onToggle = () => {
     if (this.state.expanded) {
       this.close();
     } else {
-      this.setState({ expanded: true })
+      this.setState({ expanded: true });
     }
   };
 
   render() {
     return (
-      <div
-        ref={el => (this.accordionRef = el)}
-      >
-        <Accordion
-          className="dropdown-panel-accordion"
-          asDefinitionList>
+      <div ref={el => (this.accordionRef = el)}>
+        <Accordion className="dropdown-panel-accordion" asDefinitionList>
           <AccordionItem>
             <AccordionToggle
               id={this.props.title}
@@ -53,12 +73,8 @@ class DropdownPanel extends React.Component {
             >
               {this.props.title}
             </AccordionToggle>
-            <AccordionContent
-              isHidden={!this.state.expanded}
-            >
-              <div className="options-panel pf-u-box-shadow-md">
-                {this.props.panel}
-              </div>
+            <AccordionContent isHidden={!this.state.expanded}>
+              <div className="options-panel pf-u-box-shadow-md">{this.props.panel}</div>
             </AccordionContent>
           </AccordionItem>
         </Accordion>
diff --git a/console/react/src/common/pleaseWait.js b/console/react/src/common/pleaseWait.js
new file mode 100644
index 0000000..0b63e96
--- /dev/null
+++ b/console/react/src/common/pleaseWait.js
@@ -0,0 +1,58 @@
+/*
+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.
+*/
+
+import React from "react";
+import { TextContent, Text, TextVariants } from "@patternfly/react-core";
+import PropTypes from "prop-types";
+
+import { CogIcon } from "@patternfly/react-icons";
+
+class PleaseWait extends React.Component {
+  static propTypes = {
+    isOpen: PropTypes.bool.isRequired,
+    title: PropTypes.string.isRequired,
+    message: PropTypes.string.isRequired
+  };
+
+  state = {};
+
+  render() {
+    return (
+      this.props.isOpen && (
+        <div className="topic-creating-wrapper">
+          <div id="topicCogWrapper">
+            <CogIcon id="topicCogMain" className="spinning-clockwise" color="#AAAAAA" />
+            <CogIcon id="topicCogUpper" className="spinning-cclockwise" color="#AAAAAA" />
+            <CogIcon id="topicCogLower" className="spinning-cclockwise" color="#AAAAAA" />
+          </div>
+          <TextContent>
+            <Text component={TextVariants.p}>{this.props.title}</Text>
+          </TextContent>
+          <TextContent>
+            <Text className="topic-creating-message" component={TextVariants.p}>
+              {this.props.message}
+            </Text>
+          </TextContent>
+        </div>
+      )
+    );
+  }
+}
+
+export default PleaseWait;
diff --git a/console/react/src/qdrGlobals.js b/console/react/src/common/qdrGlobals.js
similarity index 100%
rename from console/react/src/qdrGlobals.js
rename to console/react/src/common/qdrGlobals.js
diff --git a/console/react/src/updated.js b/console/react/src/common/qdrPopup.js
similarity index 73%
copy from console/react/src/updated.js
copy to console/react/src/common/qdrPopup.js
index e762d8e..091ae54 100644
--- a/console/react/src/updated.js
+++ b/console/react/src/common/qdrPopup.js
@@ -17,9 +17,9 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import React, { Component } from "react";
+import React from "react";
 
-class Updated extends Component {
+class QDRPopup extends React.Component {
   constructor(props) {
     super(props);
     this.state = {};
@@ -27,13 +27,9 @@ 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
-        )}`}
-      </pre>
+      <div aria-label="popup" dangerouslySetInnerHTML={{ __html: this.props.content }} />
     );
   }
 }
 
-export default Updated;
+export default QDRPopup;
diff --git a/console/react/src/updated.js b/console/react/src/common/qdrPopup.test.js
similarity index 63%
copy from console/react/src/updated.js
copy to console/react/src/common/qdrPopup.test.js
index e762d8e..0068397 100644
--- a/console/react/src/updated.js
+++ b/console/react/src/common/qdrPopup.test.js
@@ -17,23 +17,16 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import React, { Component } from "react";
+import React from "react";
+import { render } from "@testing-library/react";
+import QDRPopup from "./qdrPopup";
 
-class Updated extends Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
-
-  render() {
-    return (
-      <pre aria-label="last-updated" data-pf-content="true" className="overview-loading">
-        {`Updated ${this.props.service.utilities.strDate(
-          this.props.lastUpdated
-        )}`}
-      </pre>
-    );
-  }
-}
-
-export default Updated;
+it("the popup component renders HTML", () => {
+  const text = "Hello world";
+  const props = {
+    content: `<h1>${text}</h1>`
+  };
+  const { getByText, getByLabelText } = render(<QDRPopup {...props} />);
+  expect(getByLabelText("popup")).toBeInTheDocument();
+  expect(getByText("Hello world")).toBeInTheDocument();
+});
diff --git a/console/react/src/qdrService.js b/console/react/src/common/qdrService.js
similarity index 78%
rename from console/react/src/qdrService.js
rename to console/react/src/common/qdrService.js
index 8adcff8..d1387c7 100644
--- a/console/react/src/qdrService.js
+++ b/console/react/src/common/qdrService.js
@@ -24,35 +24,35 @@ import { utils } from "./amqp/utilities.js";
 const DEFAULT_INTERVAL = 5000;
 export class QDRService {
   constructor(hooks) {
-    const url = utils.getUrlParts(window.location);
-    this.management = new dm(url.protocol, DEFAULT_INTERVAL);
     this.utilities = utils;
     this.hooks = hooks;
     this.schema = null;
+    this.initManagement();
   }
 
-  setHooks = (hooks) => {
+  initManagement = () => {
+    const url = utils.getUrlParts(window.location);
+    this.management = new dm(url.protocol, DEFAULT_INTERVAL);
+  };
+  setHooks = hooks => {
     this.hooks = hooks;
-  }
+  };
 
-  onReconnect() {
-    this.management.connection.on("disconnected", this.onDisconnect.bind(this));
+  onReconnect = () => {
+    this.management.connection.on("disconnected", this.onDisconnect);
     this.hooks.setLocation("reconnect");
-  }
-  onDisconnect() {
+  };
+  onDisconnect = () => {
     this.hooks.setLocation("disconnect");
-    this.management.connection.on("connected", this.onReconnect.bind(this));
-  }
-  connect(connectOptions) {
+    this.management.connection.on("connected", this.onReconnect);
+  };
+  connect = connectOptions => {
     let self = this;
     return new Promise((resolve, reject) => {
       self.management.connection.connect(connectOptions).then(
         r => {
           // if we are ever disconnected, show the connect page and wait for a reconnect
-          self.management.connection.on(
-            "disconnected",
-            this.onDisconnect.bind(this)
-          );
+          self.management.connection.on("disconnected", this.onDisconnect);
 
           self.management.getSchema().then(schema => {
             self.schema = schema;
@@ -72,19 +72,20 @@ export class QDRService {
         }
       );
     });
-  }
-  disconnect() {
+  };
+  disconnect = () => {
     this.management.connection.disconnect();
     delete this.management;
-    const url = utils.getUrlParts(window.location);
-    this.management = new dm(url.protocol, DEFAULT_INTERVAL);
-  }
+    this.initManagement();
+  };
+
+  setReconnect = reconnect => {
+    this.management.connection.setReconnect(reconnect);
+  };
 }
 
-(function () {
-  console.dump = function (o) {
-    if (window.JSON && window.JSON.stringify)
-      console.log(JSON.stringify(o, undefined, 2));
-    else console.log(o);
+(function() {
+  console.dump = function(o) {
+    console.log(JSON.stringify(o, undefined, 2));
   };
 })();
diff --git a/console/react/src/tableToolbar.js b/console/react/src/common/tableToolbar.js
similarity index 100%
rename from console/react/src/tableToolbar.js
rename to console/react/src/common/tableToolbar.js
diff --git a/console/react/src/common/tableToolbar.test.js b/console/react/src/common/tableToolbar.test.js
new file mode 100644
index 0000000..aba4745
--- /dev/null
+++ b/console/react/src/common/tableToolbar.test.js
@@ -0,0 +1,46 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render, fireEvent } from "@testing-library/react";
+import TableToolbar from "./tableToolbar";
+
+it("should render tableToolbar", () => {
+  let handleChangeFilterValueCalled = false;
+  const props = {
+    fields: [{ title: "field0" }, { title: "field1" }],
+    filterBy: { value: "f" },
+    handleChangeFilterValue: () => (handleChangeFilterValueCalled = true),
+    total: 2,
+    page: 1,
+    perPage: 1,
+    onSetPage: () => {},
+    onPerPageSelect: () => {}
+  };
+  const { getByLabelText } = render(<TableToolbar {...props} />);
+  expect(getByLabelText("toolbar-pagination")).toBeInTheDocument();
+  const filterInput = getByLabelText("search text input");
+  expect(filterInput).toBeInTheDocument();
+
+  fireEvent.change(filterInput, { target: { value: "fi" } });
+  expect(handleChangeFilterValueCalled).toBe(true);
+
+  const paginationInput = getByLabelText("Current page");
+  expect(paginationInput).toBeInTheDocument();
+});
diff --git a/console/react/src/updated.js b/console/react/src/common/updated.js
similarity index 100%
copy from console/react/src/updated.js
copy to console/react/src/common/updated.js
diff --git a/console/react/src/updated.js b/console/react/src/common/updated.test.js
similarity index 63%
copy from console/react/src/updated.js
copy to console/react/src/common/updated.test.js
index e762d8e..885affb 100644
--- a/console/react/src/updated.js
+++ b/console/react/src/common/updated.test.js
@@ -17,23 +17,17 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import React, { Component } from "react";
+import React from "react";
+import { render } from "@testing-library/react";
+import { QDRService } from "../common/qdrService";
+import Updated from "./updated";
 
-class Updated extends Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
-
-  render() {
-    return (
-      <pre aria-label="last-updated" data-pf-content="true" className="overview-loading">
-        {`Updated ${this.props.service.utilities.strDate(
-          this.props.lastUpdated
-        )}`}
-      </pre>
-    );
-  }
-}
-
-export default Updated;
+it("should render the Updated component", () => {
+  const service = new QDRService(() => {});
+  const props = {
+    lastUpdated: new Date(),
+    service
+  };
+  const { getByLabelText } = render(<Updated {...props} />);
+  expect(getByLabelText("last-updated")).toBeInTheDocument();
+});
diff --git a/console/react/src/connect.test.js b/console/react/src/connect.test.js
deleted file mode 100644
index 8208e74..0000000
--- a/console/react/src/connect.test.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import ConnectForm from "./connect-form";
-import { QDRService } from "./qdrService";
-
-it("can create a service object", () => {
-  const service = new QDRService(() => { });
-});
-
-it("renders the connect form", () => {
-  render(
-    <ConnectForm />
-  );
-});
-
-it("connect form can be submitted", () => {
-  let connectFormRef = null;
-  const service = new QDRService(() => { });
-  const handleConnect = (fromPath, r) => { };
-  const handleAddNotification = (section, message, date, severity) => { }
-  render(
-    <ConnectForm
-      ref={el => (connectFormRef = el)}
-      service={service}
-      handleConnect={handleConnect}
-      handleAddNotification={handleAddNotification}
-      fromPath={"/test"}
-    />
-  );
-  connectFormRef.handleConnect();
-});
diff --git a/console/react/src/connect-form.js b/console/react/src/connect/connect-form.js
similarity index 61%
rename from console/react/src/connect-form.js
rename to console/react/src/connect/connect-form.js
index 009af85..e19d85a 100644
--- a/console/react/src/connect-form.js
+++ b/console/react/src/connect/connect-form.js
@@ -1,18 +1,22 @@
 /*
- * Copyright 2019 Red Hat Inc.
- *
- * Licensed 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.
- */
+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.
+*/
+
 import React from "react";
 import {
   Form,
@@ -24,7 +28,7 @@ import {
   Text,
   TextVariants
 } from "@patternfly/react-core";
-import PleaseWait from "./pleaseWait";
+import PleaseWait from "../common/pleaseWait";
 const CONNECT_KEY = "QDRSettings";
 
 class ConnectForm extends React.Component {
@@ -37,7 +41,8 @@ class ConnectForm extends React.Component {
       username: "",
       password: "",
       connecting: false,
-      connectError: null
+      connectError: null,
+      isValid: false
     };
   }
 
@@ -55,18 +60,38 @@ class ConnectForm extends React.Component {
       savedValues = JSON.parse(savedValues);
       savedValues.connectError = null;
     }
-    this.setState(savedValues);
+    this.setState(savedValues, () => {
+      this.setState({ isValid: this.validate() });
+    });
+    document.addEventListener("keypress", this.handleKeyPress);
+    this.validate();
   };
 
+  componentWillUnmount() {
+    document.removeEventListener("keypress", this.handleKeyPress);
+  }
+
+  handleKeyPress = event => {
+    if (this.validate()) {
+      if (event.code === "Enter") {
+        this.handleConnect();
+      }
+    }
+  };
+
+  validate = () => this.state.address !== "" && this.state.port !== "";
+
   handleTextInputChange = (field, value) => {
     const formValues = Object.assign(this.state);
     formValues[field] = value;
     this.setState(formValues, () => {
-      const state2Save = JSON.parse(JSON.stringify(formValues));
-      // don't save the password
-      state2Save.password = "";
-      state2Save.connectError = null;
-      localStorage.setItem(CONNECT_KEY, JSON.stringify(state2Save));
+      this.setState({ isValid: this.validate() }, () => {
+        const state2Save = JSON.parse(JSON.stringify(formValues));
+        // don't save the password
+        state2Save.password = "";
+        state2Save.connectError = null;
+        localStorage.setItem(CONNECT_KEY, JSON.stringify(state2Save));
+      });
     });
   };
 
@@ -78,9 +103,9 @@ class ConnectForm extends React.Component {
       const connectOptions = JSON.parse(JSON.stringify(this.state));
       if (connectOptions.username === "") connectOptions.username = undefined;
       if (connectOptions.password === "") connectOptions.password = undefined;
-      connectOptions.reconnect = true;
 
       this.setState({ connecting: true }, () => {
+        connectOptions.reconnect = true;
         this.props.service.connect(connectOptions).then(
           r => {
             this.setState({ connecting: false });
@@ -88,6 +113,7 @@ class ConnectForm extends React.Component {
           },
           e => {
             console.log(e);
+            this.props.service.setReconnect(false);
             this.setState({ connecting: false, connectError: e.message });
             this.props.handleAddNotification(
               "action",
@@ -106,11 +132,19 @@ class ConnectForm extends React.Component {
   };
 
   render() {
-    const { address, port, username, password, connecting, connectError } = this.state;
+    const {
+      address,
+      port,
+      username,
+      password,
+      connecting,
+      connectError,
+      isValid
+    } = this.state;
     return this.props.isConnectFormOpen ? (
       <div>
         <div className="connect-modal">
-          <div className={connecting ? "connecting" : ""}>
+          <div className={this.props.connecting || connecting ? "connecting" : ""}>
             <Form isHorizontal>
               <TextContent className="connect-title">
                 <Text component={TextVariants.h1}>Connect</Text>
@@ -118,7 +152,11 @@ class ConnectForm extends React.Component {
                   Enter the address and an HTTP-enabled port of a qpid dispatch router.
                 </Text>
               </TextContent>
-              <FormGroup label="Address" isRequired fieldId={`form-address-${this.props.prefix}`}>
+              <FormGroup
+                label="Address"
+                isRequired
+                fieldId={`form-address-${this.props.prefix}`}
+              >
                 <TextInput
                   value={address}
                   isRequired
@@ -129,7 +167,11 @@ class ConnectForm extends React.Component {
                   onChange={value => this.handleTextInputChange("address", value)}
                 />
               </FormGroup>
-              <FormGroup label="Port" isRequired fieldId={`form-port-${this.props.prefix}`}>
+              <FormGroup
+                label="Port"
+                isRequired
+                fieldId={`form-port-${this.props.prefix}`}
+              >
                 <TextInput
                   value={port}
                   onChange={value => this.handleTextInputChange("port", value)}
@@ -163,19 +205,27 @@ class ConnectForm extends React.Component {
                 </TextContent>
               )}
               <ActionGroup>
-                <Button variant="primary" data-testid="connect-button" onClick={this.handleConnect}>
+                <Button
+                  variant={this.props.isConnected || isValid ? "primary" : "danger"}
+                  isDisabled={this.props.isConnected ? false : !isValid}
+                  data-testid="connect-button"
+                  onClick={this.handleConnect}
+                >
                   {this.props.isConnected ? "Disconnect" : "Connect"}
                 </Button>
                 <Button variant="secondary" onClick={this.toggleDrawerHide}>
                   Cancel
                 </Button>
+                <input type="submit" style={{ display: "none" }} />
               </ActionGroup>
             </Form>
           </div>
           <PleaseWait
-            isOpen={connecting}
-            title="Connecting"
-            message="Connecting to the router, please wait..."
+            isOpen={this.props.connecting || connecting}
+            title={this.props.connectingTitle || "Connecting"}
+            message={
+              this.props.connectingMessage || "Connecting to the router, please wait..."
+            }
           />
         </div>
       </div>
diff --git a/console/react/src/connect/connect-form.test.js b/console/react/src/connect/connect-form.test.js
new file mode 100644
index 0000000..ba1eb1e
--- /dev/null
+++ b/console/react/src/connect/connect-form.test.js
@@ -0,0 +1,51 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render, fireEvent } from "@testing-library/react";
+import ConnectForm from "./connect-form";
+import { service, login, TEST_PORT } from "../serviceTest";
+
+it("renders the connect form", () => {
+  render(<ConnectForm />);
+});
+
+it("connect form can be submitted", async () => {
+  const props = {
+    service,
+    handleConnect: (section, message, date, severity) => {},
+    handleConnectCancel: () => {},
+    handleAddNotification: () => {},
+    fromPath: "/test",
+    prefix: "test",
+    isConnectFormOpen: true,
+    isConnected: false,
+    connecting: false
+  };
+
+  const { getByLabelText, getByTestId } = render(<ConnectForm {...props} />);
+
+  // fill out the form
+  fireEvent.change(getByLabelText(/address/i), { target: { value: "localhost" } });
+  fireEvent.change(getByLabelText(/port/i), {
+    target: { value: TEST_PORT || 5673 }
+  });
+
+  fireEvent.click(getByTestId("connect-button"));
+});
diff --git a/console/react/src/connect/connectPage.js b/console/react/src/connect/connectPage.js
new file mode 100644
index 0000000..dda2034
--- /dev/null
+++ b/console/react/src/connect/connectPage.js
@@ -0,0 +1,100 @@
+/*
+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.
+*/
+
+import React from "react";
+import {
+  PageSection,
+  PageSectionVariants,
+  TextContent,
+  Text
+} from "@patternfly/react-core";
+import ConnectForm from "./connect-form";
+
+class ConnectPage extends React.Component {
+  constructor(props) {
+    super(props);
+    this.state = { showForm: true };
+  }
+
+  handleConnectCancel = () => {
+    this.setState({ showForm: false });
+  };
+
+  shouldComponentUpdate = (nextProps, nextState) => {
+    if (nextState.showForm !== this.state.showForm) return true;
+    const nextPathname =
+      nextProps.location && nextProps.location.state && nextProps.location.state.pathname
+        ? nextProps.location.state.pathname
+        : undefined;
+    const currentPathname =
+      this.props.location &&
+      this.props.location.state &&
+      this.props.location.state.pathname
+        ? this.props.location.state.pathname
+        : undefined;
+
+    return nextPathname !== currentPathname;
+  };
+
+  render() {
+    const { showForm } = this.state;
+    const { from } = this.props.location.state || { from: { pathname: "/" } };
+    return (
+      <PageSection variant={PageSectionVariants.light} className="connect-page">
+        {showForm ? (
+          <ConnectForm
+            prefix="form"
+            service={this.props.service}
+            handleConnect={this.props.handleConnect}
+            handleConnectCancel={this.handleConnectCancel}
+            handleAddNotification={this.props.handleAddNotification}
+            fromPath={from.pathname}
+            isConnectFormOpen={true}
+            connecting={this.props.connecting}
+            connectingTitle={this.props.connectingTitle}
+            connectingMessage={this.props.connectingMessage}
+            isConnected={false}
+          />
+        ) : (
+          <React.Fragment />
+        )}
+        <div className="left-content">
+          <TextContent>
+            <Text component="h1" className="console-banner">
+              {this.props.config.title}
+            </Text>
+          </TextContent>
+          <TextContent>
+            <Text component="h2" className="connect-description">
+              The console displays information about a qpid dispatch router network. It
+              allows monitoring and management control of the router's entities.
+            </Text>
+            <Text component="h2" className="connect-description">
+              The console only provides limited information about the clients that are
+              attached to the router network and is therfore more appropriate for
+              administrators needing to know the layout and health of the router network.
+            </Text>
+          </TextContent>
+        </div>
+      </PageSection>
+    );
+  }
+}
+
+export default ConnectPage;
diff --git a/console/react/src/updated.js b/console/react/src/connect/connectPage.test.js
similarity index 62%
copy from console/react/src/updated.js
copy to console/react/src/connect/connectPage.test.js
index e762d8e..4396eed 100644
--- a/console/react/src/updated.js
+++ b/console/react/src/connect/connectPage.test.js
@@ -17,23 +17,13 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import React, { Component } from "react";
+import React from "react";
+import { render } from "@testing-library/react";
+import ConnectPage from "./connectPage";
 
-class Updated extends Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
-
-  render() {
-    return (
-      <pre aria-label="last-updated" data-pf-content="true" className="overview-loading">
-        {`Updated ${this.props.service.utilities.strDate(
-          this.props.lastUpdated
-        )}`}
-      </pre>
-    );
-  }
-}
-
-export default Updated;
+it("renders the connect page", () => {
+  const locationState = { location: { state: { pathname: "/fromTest", from: "/test" } } };
+  const config = { title: "Qpid Dispatch Router Test Console" };
+  const { getByText } = render(<ConnectPage config={config} location={locationState} />);
+  expect(getByText(config.title)).toBeInTheDocument();
+});
diff --git a/console/react/src/connectPage.js b/console/react/src/connectPage.js
deleted file mode 100644
index 724c3f7..0000000
--- a/console/react/src/connectPage.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import React from "react";
-import { PageSection, PageSectionVariants, TextContent, Text } from "@patternfly/react-core";
-import ConnectForm from "./connect-form";
-
-class ConnectPage extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = { showForm: true };
-  }
-
-  handleConnectCancel = () => {
-    this.setState({ showForm: false });
-  };
-
-  shouldComponentUpdate = (nextProps, nextState) => {
-    if (nextState.showForm !== this.state.showForm) return true;
-    const nextPathname =
-      nextProps.location && nextProps.location.state && nextProps.location.state.pathname
-        ? nextProps.location.state.pathname
-        : undefined;
-    const currentPathname =
-      this.props.location && this.props.location.state && this.props.location.state.pathname
-        ? this.props.location.state.pathname
-        : undefined;
-
-    return nextPathname !== currentPathname;
-  };
-
-  render() {
-    const { showForm } = this.state;
-    const { from } = this.props.location.state || { from: { pathname: "/" } };
-    return (
-      <React.Fragment>
-        <PageSection variant={PageSectionVariants.light} className="connect-page">
-          {showForm ? (
-            <ConnectForm
-              prefix="form"
-              service={this.props.service}
-              handleConnect={this.props.handleConnect}
-              handleConnectCancel={this.handleConnectCancel}
-              handleAddNotification={this.props.handleAddNotification}
-              fromPath={from.pathname}
-              isConnectFormOpen={true}
-            />
-          ) : (
-            <React.Fragment />
-          )}
-          <div className="left-content">
-            <TextContent>
-              <Text component="h1" className="console-banner">
-                {this.props.config.title}
-              </Text>
-            </TextContent>
-            <TextContent>
-              <Text component="p">
-                The console is an HTML based web site that displays information about a qpid
-                dispatch router network. The console only provides limited information about the
-                clients that are attached to the router network and is therfore more appropriate for
-                administrators needing to know the layout and health of the router network.
-              </Text>
-            </TextContent>
-          </div>
-        </PageSection>
-      </React.Fragment>
-    );
-  }
-}
-
-export default ConnectPage;
diff --git a/console/react/src/connectPage.test.js b/console/react/src/connectPage.test.js
deleted file mode 100644
index 878a06e..0000000
--- a/console/react/src/connectPage.test.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import ConnectPage from "./connectPage";
-
-it("renders the connect page", () => {
-  const locationState = { location: { state: { pathname: "/fromTest", from: "/test" } } }
-  const config = { title: "Qpid Dispatch Router Test Console" }
-  const { getByText } = render(
-    <ConnectPage config={config} location={locationState} />
-  );
-  expect(getByText(config.title)).toBeInTheDocument();
-});
diff --git a/console/react/src/connectionClose.test.js b/console/react/src/connectionClose.test.js
deleted file mode 100644
index 06f8e8b..0000000
--- a/console/react/src/connectionClose.test.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from "react";
-import { render, fireEvent } from '@testing-library/react';
-import ConnectionClose from "./connectionClose";
-import { mockService } from "./qdrService.mock";
-
-it("renders the ConnectionClose component", () => {
-  let sendMethodCalled = false;
-  let handleAddNotificationCalled = false;
-  let notifyClickCalled = false;
-
-  const props = {
-    service: mockService({ onSendMethod: () => sendMethodCalled = true }),
-    handleAddNotification: () => handleAddNotificationCalled = true,
-    notifyClick: () => notifyClickCalled = true,
-    extraInfo: { rowData: { data: { name: "test record", role: "normal" } } }
-  }
-  const { getByLabelText, queryByLabelText } = render(
-    <ConnectionClose {...props} />
-  )
-
-  // the close button should be there
-  const closeButton = getByLabelText("connection-close-button");
-  expect(closeButton).toBeInTheDocument();
-
-  // the confirmation dialog should not be there
-  expect(queryByLabelText("connection-close-modal")).toBeNull();
-
-  // clicking the close button should display the confirmation dialog
-  fireEvent.click(closeButton);
-  expect(getByLabelText("connection-close-modal")).toBeInTheDocument();
-
-  const confirmButton = getByLabelText("connection-close-confirm");
-  expect(confirmButton).toBeInTheDocument();
-  fireEvent.click(confirmButton);
-
-  expect(sendMethodCalled).toBe(true);
-  setTimeout(() => {
-    expect(handleAddNotificationCalled).toBe(true);
-    expect(notifyClickCalled).toBe(true);
-  }, 1);
-
-});
diff --git a/console/react/src/contextMenu.test.js b/console/react/src/contextMenu.test.js
deleted file mode 100644
index 121f765..0000000
--- a/console/react/src/contextMenu.test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from "react";
-import { render, fireEvent } from '@testing-library/react';
-import ContextMenu from "./contextMenuComponent"
-
-it("the contextMenu component renders and calls event handlers", () => {
-  let handleContextHideClicked = false;
-  let itemActionCalled = false;
-  const props = {
-    handleContextHide: () => handleContextHideClicked = true,
-    menuItems: [{ enabled: () => true, action: () => itemActionCalled = true }],
-    contextEventData: {},
-    contextEventPosition: [-1, -1]
-  }
-  const { getByLabelText } = render(
-    <ContextMenu {...props} />
-  );
-  const menuItem = getByLabelText("context-menu-item");
-  expect(menuItem).toBeInTheDocument();
-
-  fireEvent.click(menuItem);
-  expect(handleContextHideClicked).toBe(true);
-  expect(itemActionCalled).toBe(true);
-});
diff --git a/console/react/src/details/createTablePage.test.js b/console/react/src/details/createTablePage.test.js
index 0f3cc8c..09c51ea 100644
--- a/console/react/src/details/createTablePage.test.js
+++ b/console/react/src/details/createTablePage.test.js
@@ -1,28 +1,54 @@
+/*
+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.
+*/
+
 import React from "react";
-import { render, fireEvent } from '@testing-library/react';
+import { render, fireEvent, waitForElement } from "@testing-library/react";
 import CreateTablePage from "./createTablePage";
-import { mockService } from "../qdrService.mock";
+import { service, login, TEST_PORT } from "../serviceTest";
 
-it('renders a CreateTablePage', () => {
-  let sendMethodCalled = false;
-  const service = mockService({ onSendMethod: () => sendMethodCalled = true });
+it("renders a CreateTablePage", async () => {
+  if (!TEST_PORT) {
+    console.log("using mock service");
+  }
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
+
+  const listenerName = "testListener";
   const props = {
     entity: "listener",
     service,
     schema: service.schema,
     locationState: {},
     routerId: Object.keys(service.management.topology._nodeInfo)[0],
-    handleAddNotification: () => { },
-    handleActionCancel: () => { }
-  }
-  const {
-    getByLabelText,
-    getByText
-  } = render(<CreateTablePage {...props} />);
+    handleAddNotification: () => {},
+    handleActionCancel: () => {}
+  };
+  const { getByLabelText, getByText, getByTestId } = render(
+    <CreateTablePage {...props} />
+  );
 
   // the create form should be present
-  const notificationIcon = getByLabelText("create-entity-form");
-  expect(notificationIcon).toBeInTheDocument();
+  const createForm = getByLabelText("create-entity-form");
+  expect(createForm).toBeInTheDocument();
+
+  // add a listener name
+  fireEvent.change(getByLabelText(/name/i), { target: { value: listenerName } });
 
   // there should be a create button
   const createButton = getByText("Create");
@@ -30,5 +56,4 @@ it('renders a CreateTablePage', () => {
 
   // clicking the create button should submit the method
   fireEvent.click(createButton);
-  expect(sendMethodCalled).toBe(true);
-})
\ No newline at end of file
+});
diff --git a/console/react/src/details/dataSources/addressData.js b/console/react/src/details/dataSources/addressData.js
index 13c7e97..a0e0f0f 100644
--- a/console/react/src/details/dataSources/addressData.js
+++ b/console/react/src/details/dataSources/addressData.js
@@ -19,7 +19,7 @@ under the License.
 
 import React from "react";
 import DefaultData from "./defaultData";
-import { utils } from "../../amqp/utilities";
+import { utils } from "../../common/amqp/utilities";
 
 const AddressType = ({ value, extraInfo }) => {
   const data = extraInfo.rowData.data;
diff --git a/console/react/src/details/dataSources/connectionData.js b/console/react/src/details/dataSources/connectionData.js
index 2271434..b906960 100644
--- a/console/react/src/details/dataSources/connectionData.js
+++ b/console/react/src/details/dataSources/connectionData.js
@@ -19,7 +19,7 @@ under the License.
 
 import React from "react";
 import DefaultData from "./defaultData";
-import ConnectionClose from "../../connectionClose";
+import ConnectionClose from "../../common/connectionClose";
 
 class ConnectionData extends DefaultData {
   constructor(service, schema) {
diff --git a/console/react/src/details/dataSources/defaultData.js b/console/react/src/details/dataSources/defaultData.js
index 6e4b401..d448d1d 100644
--- a/console/react/src/details/dataSources/defaultData.js
+++ b/console/react/src/details/dataSources/defaultData.js
@@ -18,7 +18,7 @@ under the License.
 */
 
 import React from "react";
-import { utils } from "../../amqp/utilities";
+import { utils } from "../../common/amqp/utilities";
 import DeleteEntity from "../deleteEntity";
 import UpdateEntity from "../updateEntity";
 import CreateEntity from "../createEntity";
@@ -38,14 +38,7 @@ class DefaultData {
   schemaOperations = entity => this.schema.entityTypes[entity].operations;
 
   // emit a single button/component
-  entityAction = ({
-    component: Component,
-    props,
-    record,
-    click,
-    i,
-    asButton
-  }) => (
+  entityAction = ({ component: Component, props, record, click, i, asButton }) => (
     <Component
       key={`action-${i}`}
       record={record}
@@ -85,10 +78,7 @@ class DefaultData {
           const result = record.results.find(
             r => r[identityIndex] === currentRecord.identity
           );
-          let object = this.service.utilities.flatten(
-            record.attributeNames,
-            result
-          );
+          let object = this.service.utilities.flatten(record.attributeNames, result);
           object = this.service.utilities.formatAttributes(
             object,
             schema.entityTypes[entity]
@@ -101,9 +91,7 @@ class DefaultData {
 
   // return a list of operations allowed for this entity
   actions = entity =>
-    this.schema.entityTypes[entity].operations.filter(
-      action => action !== "READ"
-    );
+    this.schema.entityTypes[entity].operations.filter(action => action !== "READ");
 
   // action button for the entityListTable
   actionButton = ({ action, props, click, record, i, asButton }) =>
@@ -130,17 +118,13 @@ class DefaultData {
   // called by entityListTable to get the list of records
   doFetch = (page, perPage, routerId, entity) => {
     return new Promise(resolve => {
-      this.service.management.topology.fetchEntities(
-        routerId,
-        { entity },
-        results => {
-          const data = utils.flattenAll(results[routerId][entity], f => {
-            f.routerId = routerId;
-            return f;
-          });
-          resolve({ data, page, perPage });
-        }
-      );
+      this.service.management.topology.fetchEntities(routerId, { entity }, results => {
+        const data = utils.flattenAll(results[routerId][entity], f => {
+          f.routerId = routerId;
+          return f;
+        });
+        resolve({ data, page, perPage });
+      });
     });
   };
 }
diff --git a/console/react/src/detailsTablePage.js b/console/react/src/details/detailsTablePage.js
similarity index 84%
rename from console/react/src/detailsTablePage.js
rename to console/react/src/details/detailsTablePage.js
index dcc4492..b216f18 100644
--- a/console/react/src/detailsTablePage.js
+++ b/console/react/src/details/detailsTablePage.js
@@ -38,9 +38,9 @@ import {
 } from "@patternfly/react-table";
 import { Card, CardBody } from "@patternfly/react-core";
 import { Redirect } from "react-router-dom";
-import { dataMap } from "./overview/entityData";
-import { dataMap as detailsDataMap, defaultData } from "./details/entityData";
-import Updated from "./updated";
+import { dataMap } from "../overview/entityData";
+import { dataMap as detailsDataMap, defaultData } from "./entityData";
+import Updated from "../common/updated";
 
 class DetailTablesPage extends React.Component {
   constructor(props) {
@@ -76,15 +76,9 @@ class DetailTablesPage extends React.Component {
       if (this.props.details) {
         this.dataSource = !detailsDataMap[this.entity]
           ? new defaultData(this.props.service, this.props.schema)
-          : new detailsDataMap[this.entity](
-            this.props.service,
-            this.props.schema
-          );
+          : new detailsDataMap[this.entity](this.props.service, this.props.schema);
       } else {
-        this.dataSource = new dataMap[this.entity](
-          this.props.service,
-          this.props.schema
-        );
+        this.dataSource = new dataMap[this.entity](this.props.service, this.props.schema);
       }
     }
   }
@@ -101,9 +95,7 @@ class DetailTablesPage extends React.Component {
   };
 
   locationState = () => {
-    return this.props.details
-      ? this.props.locationState
-      : this.props.location.state;
+    return this.props.details ? this.props.locationState : this.props.location.state;
   };
 
   update = () => {
@@ -133,11 +125,7 @@ class DetailTablesPage extends React.Component {
         reject("no data source");
       }
       this.dataSource
-        .fetchRecord(
-          this.locationState().currentRecord,
-          this.props.schema,
-          this.entity
-        )
+        .fetchRecord(this.locationState().currentRecord, this.props.schema, this.entity)
         .then(data => {
           for (const attribute in data) {
             if (
@@ -191,30 +179,27 @@ class DetailTablesPage extends React.Component {
         this.entity,
         this.props,
         this.locationState().currentRecord,
-        event =>
-          this.handleActionClicked(event, this.locationState().currentRecord)
+        event => this.handleActionClicked(event, this.locationState().currentRecord)
       );
     };
 
     return (
       <React.Fragment>
-        <PageSection
-          variant={PageSectionVariants.light}
-          className="overview-table-page"
-        >
+        <PageSection variant={PageSectionVariants.light} className="overview-table-page">
           <Stack>
             <StackItem className="overview-header details">
               <Breadcrumb>
-                <BreadcrumbItem
-                  className="link-button"
-                  onClick={this.breadcrumbSelected}
-                >
+                <BreadcrumbItem className="link-button" onClick={this.breadcrumbSelected}>
                   {this.icap(this.entity)}
                 </BreadcrumbItem>
               </Breadcrumb>
 
               <TextContent className="details-table-header">
-                <Text className="overview-title" component={TextVariants.h1}>
+                <Text
+                  data-testid={`detail-for-${this.parentItem()}`}
+                  className="overview-title"
+                  component={TextVariants.h1}
+                >
                   {this.parentItem()}
                 </Text>
                 {!this.props.details && (
diff --git a/console/react/src/details/dataSources/addressData.js b/console/react/src/details/detailsTablePage.test.js
similarity index 56%
copy from console/react/src/details/dataSources/addressData.js
copy to console/react/src/details/detailsTablePage.test.js
index 13c7e97..9f49d38 100644
--- a/console/react/src/details/dataSources/addressData.js
+++ b/console/react/src/details/detailsTablePage.test.js
@@ -18,28 +18,19 @@ under the License.
 */
 
 import React from "react";
-import DefaultData from "./defaultData";
-import { utils } from "../../amqp/utilities";
+import { render } from "@testing-library/react";
+import DetailsTablePage from "./detailsTablePage";
 
-const AddressType = ({ value, extraInfo }) => {
-  const data = extraInfo.rowData.data;
-  const identity = utils.identity_clean(data.identity);
-  const cls = utils.addr_class(identity);
-
-  return (
-    <span className="entity-type">
-      <i className={`address-${cls}`}></i>
-      {cls}
-    </span>
-  );
-};
-
-class AddressData extends DefaultData {
-  constructor(service, schema) {
-    super(service, schema);
-    this.typeFormatter = AddressType;
-    this.detailName = "router.address";
-  }
-}
-
-export default AddressData;
+it("renders the detailsTablePage", () => {
+  const entity = "testEntity";
+  const props = {
+    entity,
+    locationState: { currentRecord: { name: "test" } },
+    details: true,
+    schema: { entityTypes: { testEntity: { attributes: [], operations: [] } } },
+    service: { management: { topology: { fetchEntities: () => Promise.resolve([]) } } }
+  };
+  const { getByLabelText } = render(<DetailsTablePage {...props} />);
+  const table = getByLabelText(entity);
+  expect(table).toBeInTheDocument();
+});
diff --git a/console/react/src/details/enitiesPage.js b/console/react/src/details/entitiesPage.js
similarity index 91%
rename from console/react/src/details/enitiesPage.js
rename to console/react/src/details/entitiesPage.js
index 6416b62..81d1916 100644
--- a/console/react/src/details/enitiesPage.js
+++ b/console/react/src/details/entitiesPage.js
@@ -22,13 +22,13 @@ import { PageSection, PageSectionVariants } from "@patternfly/react-core";
 import { Stack, StackItem } from "@patternfly/react-core";
 import { Split, SplitItem } from "@patternfly/react-core";
 
-import DetailsTablePage from "../detailsTablePage";
+import DetailsTablePage from "./detailsTablePage";
 import UpdateTablePage from "./updateTablePage";
 import CreateTablePage from "./createTablePage";
 import EntityListTable from "./entityListTable";
 import EntityList from "./entityList";
 import RouterSelect from "./routerSelect";
-import Updated from "../updated";
+import Updated from "../common/updated";
 
 class EntitiesPage extends React.Component {
   constructor(props) {
@@ -58,20 +58,11 @@ class EntitiesPage extends React.Component {
     this.setState({ entity, showTable: "entities" });
   };
 
-  fixNull = rec => {
-    for (const attr in rec) {
-      if (rec[attr] === null) {
-        rec[attr] = "";
-      }
-      return rec;
-    };
-  }
-
   handleEntityAction = (action, record) => {
     if (action === "Done") action = "entities";
     this.setState({
       actionState: {
-        currentRecord: this.fixNull(record),
+        currentRecord: record,
         entity: this.props.entity
       },
       showTable: action
@@ -85,16 +76,12 @@ class EntitiesPage extends React.Component {
     if (!props.locationState.currentRecord) {
       this.handleSwitchEntity(this.state.entity);
     } else {
-      this.handleDetailClick(
-        props.locationState.currentRecord.name,
-        extraInfo,
-        {
-          page,
-          sortBy,
-          filterBy,
-          perPage
-        }
-      );
+      this.handleDetailClick(props.locationState.currentRecord.name, extraInfo, {
+        page,
+        sortBy,
+        filterBy,
+        perPage
+      });
     }
   };
 
@@ -184,10 +171,7 @@ class EntitiesPage extends React.Component {
     };
 
     return (
-      <PageSection
-        variant={PageSectionVariants.light}
-        className="details-table-page"
-      >
+      <PageSection variant={PageSectionVariants.light} className="details-table-page">
         <Stack>
           <StackItem className="details-header">
             <Split>
diff --git a/console/react/src/details/entitiesPage.test.js b/console/react/src/details/entitiesPage.test.js
new file mode 100644
index 0000000..8b5e012
--- /dev/null
+++ b/console/react/src/details/entitiesPage.test.js
@@ -0,0 +1,50 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render, waitForElement, fireEvent } from "@testing-library/react";
+import { service, login, TEST_PORT } from "../serviceTest";
+import EntitiesPage from "./entitiesPage";
+
+it("renders an EntitiesPage", async () => {
+  if (!TEST_PORT) {
+    console.log("using mock service");
+  }
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
+
+  const props = {
+    service,
+    schema: service.schema
+  };
+  let pageRef = null;
+  const { getByTestId } = render(<EntitiesPage ref={el => (pageRef = el)} {...props} />);
+
+  // force page to show the router entity list
+  pageRef.handleSelectEntity("router");
+
+  // the router A should be in the list
+  await waitForElement(() => getByTestId("A"));
+
+  // click on the A
+  fireEvent.click(getByTestId("A"));
+
+  // the details page should show for router A
+  await waitForElement(() => getByTestId("detail-for-A"));
+});
diff --git a/console/react/src/details/entityList.js b/console/react/src/details/entityList.js
index dff5d78..6859def 100644
--- a/console/react/src/details/entityList.js
+++ b/console/react/src/details/entityList.js
@@ -36,7 +36,7 @@ class EntityList extends React.Component {
       li => !this.exclude.includes(li)
     );
 
-    this.cleanList.sort((a, b) => (a > b ? 1 : a < b ? -1 : 0));
+    this.cleanList.sort();
   }
 
   handleSelectEntity = event => {
@@ -50,6 +50,7 @@ class EntityList extends React.Component {
       <List className="entities-list pf-u-box-shadow-sm-right">
         {this.cleanList.map(entity => (
           <ListItem
+            data-testid={entity}
             key={entity}
             onClick={this.handleSelectEntity}
             className={entity === this.state.entity ? "selected" : ""}
diff --git a/console/react/src/details/dataSources/connectionData.js b/console/react/src/details/entityList.test.js
similarity index 53%
copy from console/react/src/details/dataSources/connectionData.js
copy to console/react/src/details/entityList.test.js
index 2271434..40b43f7 100644
--- a/console/react/src/details/dataSources/connectionData.js
+++ b/console/react/src/details/entityList.test.js
@@ -18,33 +18,28 @@ under the License.
 */
 
 import React from "react";
-import DefaultData from "./defaultData";
-import ConnectionClose from "../../connectionClose";
+import { render, fireEvent } from "@testing-library/react";
+import { service, login, TEST_PORT } from "../serviceTest";
+import EntityList from "./entityList";
 
-class ConnectionData extends DefaultData {
-  constructor(service, schema) {
-    super(service, schema);
-    this.extraFields = [
-      {
-        title: "",
-        field: "connection",
-        noSort: true,
-        formatter: ConnectionClose
-      }
-    ];
-    this.detailEntity = "router.link";
-    this.detailName = "Link";
+it("renders a EntityList", async () => {
+  if (!TEST_PORT) {
+    console.log("using mock service");
   }
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
 
-  detailActions = (entity, props, record) => {
-    return (
-      <ConnectionClose
-        asButton={true}
-        extraInfo={{ rowData: { data: record } }}
-        {...props}
-      />
-    );
+  const props = {
+    service,
+    schema: service.schema,
+    handleSelectEntity: () => {}
   };
-}
+  const { getByTestId } = render(<EntityList {...props} />);
 
-export default ConnectionData;
+  // the log item should be there
+  const logEntity = getByTestId("log");
+  expect(logEntity).toBeInTheDocument();
+
+  // clicking on the log entity should not crash
+  fireEvent.click(logEntity);
+});
diff --git a/console/react/src/details/entityListTable.js b/console/react/src/details/entityListTable.js
index c494540..403dc63 100644
--- a/console/react/src/details/entityListTable.js
+++ b/console/react/src/details/entityListTable.js
@@ -28,15 +28,13 @@ import {
 } from "@patternfly/react-table";
 import { Button, Pagination } from "@patternfly/react-core";
 import { Redirect } from "react-router-dom";
-import TableToolbar from "../tableToolbar";
+import TableToolbar from "../common/tableToolbar";
 import { dataMap, defaultData } from "./entityData";
 
 // If the breadcrumb on the detailsTablePage was used to return to this page,
 // we will have saved state info in props.location.state
 const propFromLocation = (props, which, defaultValue) => {
-  return props &&
-    props.detailsState &&
-    typeof props.detailsState[which] !== "undefined"
+  return props && props.detailsState && typeof props.detailsState[which] !== "undefined"
     ? props.detailsState[which]
     : defaultValue;
 };
@@ -149,9 +147,7 @@ class EntityListTable extends React.Component {
       if (!this.mounted) return;
       const { rows, page, total, allRows } = sliced;
       allRows.forEach(row => {
-        const prevRow = this.state.allRows.find(
-          r => r.data.name === row.data.name
-        );
+        const prevRow = this.state.allRows.find(r => r.data.name === row.data.name);
         if (prevRow && prevRow.selected) {
           row.selected = true;
         }
@@ -173,6 +169,7 @@ class EntityListTable extends React.Component {
     }
     return (
       <Button
+        data-testid={value}
         className="link-button"
         onClick={() => this.detailClick(value, extraInfo)}
       >
@@ -395,10 +392,7 @@ class EntityListTable extends React.Component {
     const tableProps = {
       cells: this.columns,
       rows: this.state.rows,
-      actions: this.dataSource.actionMenuItems(
-        this.props.entity,
-        this.handleAction
-      ),
+      actions: this.dataSource.actionMenuItems(this.props.entity, this.handleAction),
       "aria-label": this.props.entity,
       sortBy: this.state.sortBy,
       onSort: this.onSort,
diff --git a/console/react/src/details/routerSelect.js b/console/react/src/details/routerSelect.js
index b755df4..0d3bec0 100644
--- a/console/react/src/details/routerSelect.js
+++ b/console/react/src/details/routerSelect.js
@@ -1,10 +1,29 @@
+/*
+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.
+*/
+
 import React from "react";
 import {
   OptionsMenu,
   OptionsMenuItem,
   OptionsMenuToggleWithText
 } from "@patternfly/react-core";
-import { utils } from "../amqp/utilities";
+import { utils } from "../common/amqp/utilities";
 
 class RouterSelect extends React.Component {
   constructor(props) {
@@ -57,10 +76,7 @@ class RouterSelect extends React.Component {
     ));
 
     const toggle = (
-      <OptionsMenuToggleWithText
-        toggleText={selectedOption}
-        onToggle={this.onToggle}
-      />
+      <OptionsMenuToggleWithText toggleText={selectedOption} onToggle={this.onToggle} />
     );
 
     return (
diff --git a/console/react/src/details/schema/schemaPage.test.js b/console/react/src/details/schema/schemaPage.test.js
index 7954c8e..84e8923 100644
--- a/console/react/src/details/schema/schemaPage.test.js
+++ b/console/react/src/details/schema/schemaPage.test.js
@@ -1,6 +1,25 @@
+/*
+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.
+*/
+
 import React from "react";
 import { render, fireEvent } from "@testing-library/react";
-import { mockService } from "../../qdrService.mock";
+import { mockService } from "../../../test_data/qdrService.mock";
 import SchemaPage from "./schemaPage";
 
 it("renders a SchemaPage", () => {
diff --git a/console/react/src/details/updateTablePage.js b/console/react/src/details/updateTablePage.js
index 86c4a29..c893698 100644
--- a/console/react/src/details/updateTablePage.js
+++ b/console/react/src/details/updateTablePage.js
@@ -44,7 +44,7 @@ import { cellWidth } from "@patternfly/react-table";
 import { Card, CardBody } from "@patternfly/react-core";
 import { Redirect } from "react-router-dom";
 import { dataMap as detailsDataMap, defaultData } from "./entityData";
-import { utils } from "../amqp/utilities";
+import { utils } from "../common/amqp/utilities";
 
 class UpdateTablePage extends React.Component {
   constructor(props) {
@@ -64,10 +64,7 @@ class UpdateTablePage extends React.Component {
     } else {
       this.dataSource = !detailsDataMap[this.entity]
         ? new defaultData(this.props.service, this.props.schema)
-        : new detailsDataMap[this.entity](
-          this.props.service,
-          this.props.schema
-        );
+        : new detailsDataMap[this.entity](this.props.service, this.props.schema);
     }
 
     this.state = {
@@ -85,11 +82,26 @@ class UpdateTablePage extends React.Component {
       redirectPath: "/dashboard",
       lastUpdated: new Date(),
       changes: false,
-      record: this.props.locationState.currentRecord
+      record: this.fixNull(this.props.locationState.currentRecord)
     };
     this.originalRecord = utils.copy(this.state.record);
   }
 
+  fixNull = rec => {
+    const record = utils.copy(rec);
+    const attributes = this.dataSource.schemaAttributes(this.entity);
+    for (const attr in record) {
+      if (record[attr] === null) {
+        if (attributes[attr].type === "string") {
+          record[attr] = "";
+        } else if (attributes[attr].type === "integer") {
+          record[attr] = 0;
+        }
+      }
+    }
+    return record;
+  };
+
   handleTextInputChange = (value, key) => {
     const { record } = this.state;
     record[key] = value;
@@ -112,14 +124,6 @@ class UpdateTablePage extends React.Component {
       if (type === "list") readOnly = true;
       if (type === "integer" && attribute.graph) readOnly = true;
       let required = attribute.required;
-      if (record[attributeKey] === null) {
-        if (attribute.type === "string")
-          record[attributeKey] = "";
-        if (attribute.type === "boolean")
-          record[attributeKey] = false;
-        if (attribute.type === "integer")
-          record[attributeKey] = 0;
-      }
       const value = record[attributeKey];
       if (
         this.dataSource.updateMetaData &&
@@ -154,9 +158,7 @@ class UpdateTablePage extends React.Component {
                 aria-describedby="entiy-form-field"
                 name={attributeKey}
                 isDisabled={readOnly}
-                onChange={value =>
-                  this.handleTextInputChange(value, attributeKey)
-                }
+                onChange={value => this.handleTextInputChange(value, attributeKey)}
               />
             </FormGroup>
           );
@@ -165,9 +167,7 @@ class UpdateTablePage extends React.Component {
             <FormGroup {...formGroupProps} key={attributeKey}>
               <FormSelect
                 value={value}
-                onChange={value =>
-                  this.handleTextInputChange(value, attributeKey)
-                }
+                onChange={value => this.handleTextInputChange(value, attributeKey)}
                 id={id}
                 name={attributeKey}
               >
@@ -186,12 +186,8 @@ class UpdateTablePage extends React.Component {
           formGroups.push(
             <FormGroup {...formGroupProps} key={attributeKey}>
               <Checkbox
-                isChecked={
-                  record[attributeKey] === null ? false : record[attributeKey]
-                }
-                onChange={value =>
-                  this.handleTextInputChange(value, attributeKey)
-                }
+                isChecked={record[attributeKey] === null ? false : record[attributeKey]}
+                onChange={value => this.handleTextInputChange(value, attributeKey)}
                 label={attributeKey}
                 id={id}
                 name={attributeKey}
@@ -225,27 +221,24 @@ class UpdateTablePage extends React.Component {
     const attributes = {};
     // identity is needed to update the record
     attributes["identity"] = record.identity;
+    const schemaAttributes = this.dataSource.schemaAttributes(this.entity);
     // pass any other attributes that have changed
     for (const attr in record) {
       if (record[attr] !== this.originalRecord[attr]) {
         attributes[attr] = record[attr];
       }
+      if (schemaAttributes[attr] && schemaAttributes[attr].required) {
+        attributes[attr] = record[attr];
+      }
       if (attr === "outputFile") {
-        attributes["outputFile"] =
-          record.outputFile === "" ? null : record.outputFile;
+        attributes["outputFile"] = record.outputFile === "" ? null : record.outputFile;
       }
     }
     // call update
     this.props.service.management.connection
-      .sendMethod(
-        record.routerId || record.nodeId,
-        this.entity,
-        attributes,
-        "UPDATE"
-      )
+      .sendMethod(record.routerId || record.nodeId, this.entity, attributes, "UPDATE")
       .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 = `Updated ${record.name} failed with message: ${results.context.message.application_properties.statusDescription}`;
           console.log(`error ${msg}`);
@@ -253,12 +246,7 @@ class UpdateTablePage extends React.Component {
         } else {
           const msg = `Updated ${this.props.entity} ${record.name}`;
           console.log(`success ${msg}`);
-          this.props.handleAddNotification(
-            "action",
-            msg,
-            new Date(),
-            "success"
-          );
+          this.props.handleAddNotification("action", msg, new Date(), "success");
         }
         const props = this.props;
         props.locationState.currentRecord = record;
@@ -280,17 +268,11 @@ class UpdateTablePage extends React.Component {
 
     return (
       <React.Fragment>
-        <PageSection
-          variant={PageSectionVariants.light}
-          className="overview-table-page"
-        >
+        <PageSection variant={PageSectionVariants.light} className="overview-table-page">
           <Stack>
             <StackItem className="overview-header details">
               <Breadcrumb>
-                <BreadcrumbItem
-                  className="link-button"
-                  onClick={this.breadcrumbSelected}
-                >
+                <BreadcrumbItem className="link-button" onClick={this.breadcrumbSelected}>
                   {this.icap(this.entity)}
                 </BreadcrumbItem>
               </Breadcrumb>
@@ -306,10 +288,7 @@ class UpdateTablePage extends React.Component {
                   >
                     Cancel
                   </Button>
-                  <Button
-                    onClick={this.handleUpdate}
-                    isDisabled={!this.state.changes}
-                  >
+                  <Button onClick={this.handleUpdate} isDisabled={!this.state.changes}>
                     Update
                   </Button>
                 </ActionGroup>
@@ -318,7 +297,9 @@ class UpdateTablePage extends React.Component {
             <StackItem id="update-form">
               <Card>
                 <CardBody>
-                  <Form isHorizontal aria-label="update-entity-form">{this.schemaToForm()}</Form>
+                  <Form isHorizontal aria-label="update-entity-form">
+                    {this.schemaToForm()}
+                  </Form>
                 </CardBody>
               </Card>
             </StackItem>
diff --git a/console/react/src/details/updateTablePage.test.js b/console/react/src/details/updateTablePage.test.js
index c2bb740..270669c 100644
--- a/console/react/src/details/updateTablePage.test.js
+++ b/console/react/src/details/updateTablePage.test.js
@@ -1,24 +1,40 @@
+/*
+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.
+*/
+
 import React from "react";
-import { render, fireEvent } from '@testing-library/react';
-import { mockService } from "../qdrService.mock";
+import { render, fireEvent } from "@testing-library/react";
+import { mockService } from "../../test_data/qdrService.mock";
 import UpdateTablePage from "./updateTablePage";
 
-it('renders a UpdateTablePage', () => {
+it("renders a UpdateTablePage", () => {
   let sendMethodCalled = false;
-  const service = mockService({ onSendMethod: () => sendMethodCalled = true });
+  const service = mockService({ onSendMethod: () => (sendMethodCalled = true) });
   const props = {
     entity: "log",
     service,
     schema: service.schema,
     locationState: { currentRecord: { name: "test.log.name", enable: "" } },
     routerId: Object.keys(service.management.topology._nodeInfo)[0],
-    handleAddNotification: () => { },
-    handleActionCancel: () => { }
-  }
-  const {
-    getByLabelText,
-    getByText
-  } = render(<UpdateTablePage {...props} />);
+    handleAddNotification: () => {},
+    handleActionCancel: () => {}
+  };
+  const { getByLabelText, getByText } = render(<UpdateTablePage {...props} />);
 
   // the update form should be present
   expect(getByLabelText("update-entity-form")).toBeInTheDocument();
@@ -33,8 +49,7 @@ it('renders a UpdateTablePage', () => {
   expect(sendMethodCalled).toBe(false);
 
   // change a form field and try the update button again
-  fireEvent.change(getByLabelText(/enable/i), { target: { value: 'debug' } });
+  fireEvent.change(getByLabelText(/enable/i), { target: { value: "debug" } });
   fireEvent.click(updateButton);
   expect(sendMethodCalled).toBe(true);
-
-})
\ No newline at end of file
+});
diff --git a/console/react/src/detailsTablePage.test.js b/console/react/src/detailsTablePage.test.js
deleted file mode 100644
index 300678e..0000000
--- a/console/react/src/detailsTablePage.test.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import DetailsTablePage from "./detailsTablePage";
-
-it("renders the detailsTablePage", () => {
-  const entity = "testEntity"
-  const props = {
-    entity,
-    locationState: { currentRecord: { name: "test" } },
-    details: true,
-    schema: { entityTypes: { testEntity: { attributes: [], operations: [] } } },
-    service: { management: { topology: { fetchEntities: () => Promise.resolve([]) } } }
-  }
-  const { getByLabelText } = render(
-    <DetailsTablePage {...props} />
-  );
-  const table = getByLabelText(entity);
-  expect(table).toBeInTheDocument();
-});
diff --git a/console/react/src/index.css b/console/react/src/index.css
deleted file mode 100644
index 4a1df4d..0000000
--- a/console/react/src/index.css
+++ /dev/null
@@ -1,13 +0,0 @@
-body {
-  margin: 0;
-  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
-    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
-    sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-}
-
-code {
-  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
-    monospace;
-}
diff --git a/console/react/src/index.js b/console/react/src/index.js
index 7af6900..9297040 100644
--- a/console/react/src/index.js
+++ b/console/react/src/index.js
@@ -1,14 +1,31 @@
+/*
+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.
+*/
+
 import React from "react";
 import ReactDOM from "react-dom";
 import App from "./App";
-import * as serviceWorker from "./serviceWorker";
 
 let config = { title: "Apache Qpid Dispatch Console" };
 fetch("/config.json")
   .then(res => res.json())
   .then(cfg => {
     config = cfg;
-    console.log("successfully loaded console title from /config.json");
   })
   .catch(error => {
     console.log("/config.json not found. Using default console title");
@@ -16,8 +33,3 @@ fetch("/config.json")
   .finally(() =>
     ReactDOM.render(<App config={config} />, document.getElementById("root"))
   );
-
-// If you want your app to work offline and load faster, you can change
-// unregister() to register() below. Note this comes with some pitfalls.
-// Learn more about service workers: https://bit.ly/CRA-PWA
-serviceWorker.unregister();
diff --git a/console/react/src/layout.test.js b/console/react/src/layout.test.js
deleted file mode 100644
index 8f75b15..0000000
--- a/console/react/src/layout.test.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from "react";
-import { render, fireEvent } from '@testing-library/react';
-import PageLayout from "./layout";
-import { mockService } from "./qdrService.mock";
-
-it('allows the user to login successfully', async () => {
-  const title = "Test Layout Page";
-  const props = {
-    config: { title: title },
-    service: mockService({})
-  };
-
-  const {
-    getAllByText,
-    getByLabelText,
-    findByLabelText,
-    getByTestId } = render(<PageLayout {...props} />);
-
-  // the correct title should be found
-  expect(getAllByText(title));
-
-  // fill out the form
-  fireEvent.change(getByLabelText(/address/i), { target: { value: 'localhost' } });
-  fireEvent.change(getByLabelText(/port/i), { target: { value: '5673' } });
-
-  fireEvent.click(getByTestId("connect-button"));
-
-  // wait for the dashboard
-  // to show up before continuing with our assertions.
-  const dashboard = await findByLabelText('dashboard-page');
-
-  expect(dashboard).toHaveTextContent(/Router network statistics/i);
-})
\ No newline at end of file
diff --git a/console/react/src/notificationDrawer.test.js b/console/react/src/notificationDrawer.test.js
deleted file mode 100644
index 574720e..0000000
--- a/console/react/src/notificationDrawer.test.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from "react";
-import { render, fireEvent } from '@testing-library/react';
-import NotificationDrawer from "./notificationDrawer";
-
-it('renders without crashing', () => {
-  render(<NotificationDrawer />);
-})
-
-it('renders a notification icon', () => {
-  let notificationRef = null;
-  const {
-    getByLabelText,
-    getByText
-  } = render(<NotificationDrawer
-    ref={el => (notificationRef = el)}
-  />);
-
-  // the notifications icon should be present
-  const notificationIcon = getByLabelText("Notifications");
-  expect(notificationIcon).toBeInTheDocument();
-
-  // add a notification
-  const section = "action";
-  const message = "test message";
-  const timestamp = new Date();
-  const severity = "info";
-  notificationRef.addNotification({ section, message, timestamp, severity })
-
-  // click the notification icon
-  fireEvent.click(notificationIcon);
-
-  // there should now be a single notification-item
-  expect(getByText("1 new event")).toBeInTheDocument();
-})
\ No newline at end of file
diff --git a/console/react/src/overview/dashboard/activeAddressesCard.js b/console/react/src/overview/dashboard/activeAddressesCard.js
index 6ec88d2..f1c509d 100644
--- a/console/react/src/overview/dashboard/activeAddressesCard.js
+++ b/console/react/src/overview/dashboard/activeAddressesCard.js
@@ -1,3 +1,22 @@
+/*
+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.
+*/
+
 import React from "react";
 import { Table, TableHeader, TableBody } from "@patternfly/react-table";
 
@@ -41,25 +60,19 @@ class ActiveAddressesCard extends React.Component {
               aresult.attributeNames,
               aresult.results[i]
             );
-            if (result.linkType === "endpoint" && result.linkDir === "out") {
+            if (result.linkType === "endpoint" && result.linkDir === "in") {
               if (
                 parseInt(result.settleRate) > 0 &&
-                !result.owningAddr.startsWith("Ltemp.")
+                result.owningAddr && !result.owningAddr.startsWith("Ltemp.")
               ) {
                 if (!active.hasOwnProperty[result.owningAddr]) {
                   active[result.owningAddr] = {
-                    addr: this.props.service.utilities.addr_text(
-                      result.owningAddr
-                    ),
-                    cls: this.props.service.utilities.addr_class(
-                      result.owningAddr
-                    ),
+                    addr: this.props.service.utilities.addr_text(result.owningAddr),
+                    cls: this.props.service.utilities.addr_class(result.owningAddr),
                     settleRate: 0
                   };
                 }
-                active[result.owningAddr].settleRate += parseInt(
-                  result.settleRate
-                );
+                active[result.owningAddr].settleRate += parseInt(result.settleRate);
               }
             }
           }
@@ -79,9 +92,7 @@ class ActiveAddressesCard extends React.Component {
   };
 
   nextUpdateString = () => {
-    const nextUpdate = new Date(
-      this.state.lastUpdate.getTime() + UPDATE_INTERVAL
-    );
+    const nextUpdate = new Date(this.state.lastUpdate.getTime() + UPDATE_INTERVAL);
     return this.props.service.utilities.strDate(nextUpdate);
   };
 
diff --git a/console/react/src/alertList.js b/console/react/src/overview/dashboard/alertList.js
similarity index 100%
rename from console/react/src/alertList.js
rename to console/react/src/overview/dashboard/alertList.js
diff --git a/console/react/src/overview/dashboard/alertList.test.js b/console/react/src/overview/dashboard/alertList.test.js
new file mode 100644
index 0000000..55614a7
--- /dev/null
+++ b/console/react/src/overview/dashboard/alertList.test.js
@@ -0,0 +1,47 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render } from "@testing-library/react";
+import AlertList from "./alertList";
+
+it("renders the AlertList component", () => {
+  let ref = null;
+  const props = {};
+  const { getByLabelText, queryByLabelText } = render(
+    <AlertList ref={el => (ref = el)} {...props} />
+  );
+  // the container should be there
+  expect(getByLabelText("alert-list")).toBeInTheDocument();
+  // there should be no alerts in the list to start with
+  expect(queryByLabelText("alert-close-button")).toBeNull();
+
+  // add an alert
+  ref.addAlert("info", "testing");
+  // the alert close button should now be there
+  expect(getByLabelText("alert-close-button")).toBeInTheDocument();
+
+  const alert = {
+    key: 0
+  };
+  // hide the alert
+  ref.hideAlert(alert);
+  // the alert close button should now be gone
+  expect(queryByLabelText("alert-close-button")).toBeNull();
+});
diff --git a/console/react/src/overview/dashboard/chartBase.js b/console/react/src/overview/dashboard/chartBase.js
index c7478b7..5cd2727 100644
--- a/console/react/src/overview/dashboard/chartBase.js
+++ b/console/react/src/overview/dashboard/chartBase.js
@@ -25,28 +25,27 @@ import OverviewChart from "./overviewChart";
 class ChartBase extends React.Component {
   constructor(props) {
     super(props);
-    this.state = {
-      rates: []
-    };
-    this.rawData = [];
-    this.rateStorage = {};
-    this.initialized = false;
-    for (let i = 0; i < 60 * 60; i++) {
-      this.state.rates.push(0);
-    }
     this.title = "Override me";
     this.ariaLabel = "base-chart";
     this.isRate = false;
+    this.style = { fill: "#EBAEBA", fillOpacity: 1, stroke: "#EAEAEA" };
+    this.state = {
+      data: this.props.chartData.data(this.props.period)
+    };
   }
 
   componentDidMount = () => {
-    this.mounted = true;
-    this.timer = setInterval(this.updateData, 1000);
+    this.timer = setInterval(this.setData, 1000);
   };
 
   componentWillUnmount = () => {
-    this.mounted = false;
-    clearInterval(this.timer);
+    if (this.timer) {
+      clearInterval(this.timer);
+    }
+  };
+
+  setData = () => {
+    this.setState({ data: this.props.chartData.data(this.props.period) });
   };
 
   setStyle = (color, opacity) => {
@@ -56,50 +55,15 @@ class ChartBase extends React.Component {
       stroke: d3.rgb(color).darker(2)
     };
   };
-  updateData = () => {
-    console.log("updateData should be overridden");
-  };
-
-  init = datum => {
-    for (let i = 0; i < 60 * 60; i++) {
-      this.rawData.push(datum);
-    }
-    this.initialized = true;
-  };
-
-  addData = datum => {
-    if (!this.initialized) {
-      this.init(datum);
-    }
-    if (!this.mounted) return;
-    const { rates } = this.state;
-    this.rawData.push(datum);
-    this.rawData.splice(0, 1);
-    if (this.isRate) {
-      // get the average rate of change for the last three values
-      const avg = this.props.service.utilities.rates(
-        { val: datum },
-        ["val"],
-        this.rateStorage,
-        "val",
-        3
-      );
-      datum = Math.round(avg.val);
-    }
-    rates.push(datum);
-    rates.splice(0, 1);
-    this.setState({ rates });
-  };
-
-  data = () => {
-    const start = Math.max(this.state.rates.length - this.props.period, 0);
-    const end = this.state.rates.length - 1;
-    return this.state.rates.slice(start, end);
-  };
 
   render() {
     return (
-      <OverviewChart ariaLabel={this.ariaLabel} data={this.data()} title={this.title} style={this.style} />
+      <OverviewChart
+        ariaLabel={this.ariaLabel}
+        data={this.state.data}
+        title={this.title}
+        style={this.style}
+      />
     );
   }
 }
diff --git a/console/react/src/overview/dashboard/chartBase.js b/console/react/src/overview/dashboard/chartData.js
similarity index 51%
copy from console/react/src/overview/dashboard/chartBase.js
copy to console/react/src/overview/dashboard/chartData.js
index c7478b7..508efda 100644
--- a/console/react/src/overview/dashboard/chartBase.js
+++ b/console/react/src/overview/dashboard/chartData.js
@@ -17,49 +17,19 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import React from "react";
-import * as d3 from "d3";
-
-import OverviewChart from "./overviewChart";
-
-class ChartBase extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {
-      rates: []
-    };
+class ChartData {
+  constructor(service) {
+    this.service = service;
+    this.rates = [];
     this.rawData = [];
     this.rateStorage = {};
     this.initialized = false;
     for (let i = 0; i < 60 * 60; i++) {
-      this.state.rates.push(0);
+      this.rates.push(0);
     }
-    this.title = "Override me";
-    this.ariaLabel = "base-chart";
     this.isRate = false;
   }
 
-  componentDidMount = () => {
-    this.mounted = true;
-    this.timer = setInterval(this.updateData, 1000);
-  };
-
-  componentWillUnmount = () => {
-    this.mounted = false;
-    clearInterval(this.timer);
-  };
-
-  setStyle = (color, opacity) => {
-    this.style = {
-      fill: color,
-      fillOpacity: opacity || 1,
-      stroke: d3.rgb(color).darker(2)
-    };
-  };
-  updateData = () => {
-    console.log("updateData should be overridden");
-  };
-
   init = datum => {
     for (let i = 0; i < 60 * 60; i++) {
       this.rawData.push(datum);
@@ -71,13 +41,11 @@ class ChartBase extends React.Component {
     if (!this.initialized) {
       this.init(datum);
     }
-    if (!this.mounted) return;
-    const { rates } = this.state;
     this.rawData.push(datum);
     this.rawData.splice(0, 1);
     if (this.isRate) {
       // get the average rate of change for the last three values
-      const avg = this.props.service.utilities.rates(
+      const avg = this.service.utilities.rates(
         { val: datum },
         ["val"],
         this.rateStorage,
@@ -86,22 +54,11 @@ class ChartBase extends React.Component {
       );
       datum = Math.round(avg.val);
     }
-    rates.push(datum);
-    rates.splice(0, 1);
-    this.setState({ rates });
+    this.rates.push(datum);
+    this.rates.splice(0, 1);
   };
 
-  data = () => {
-    const start = Math.max(this.state.rates.length - this.props.period, 0);
-    const end = this.state.rates.length - 1;
-    return this.state.rates.slice(start, end);
-  };
-
-  render() {
-    return (
-      <OverviewChart ariaLabel={this.ariaLabel} data={this.data()} title={this.title} style={this.style} />
-    );
-  }
+  data = period => this.rates.slice(-period);
 }
 
-export default ChartBase;
+export default ChartData;
diff --git a/console/react/src/overview/dashboard/dashboardPage.js b/console/react/src/overview/dashboard/dashboardPage.js
index 7116a78..23d2221 100644
--- a/console/react/src/overview/dashboard/dashboardPage.js
+++ b/console/react/src/overview/dashboard/dashboardPage.js
@@ -80,8 +80,14 @@ class DashboardPage extends React.Component {
                 <div className="time-period">For the past {this.timePeriodString()}</div>
               </CardHeader>
               <CardBody>
-                <ThroughputChart period={this.state.timePeriod} service={this.props.service} />
-                <InflightChart period={this.state.timePeriod} service={this.props.service} />
+                <ThroughputChart
+                  period={this.state.timePeriod}
+                  chartData={this.props.throughputChartData}
+                />
+                <InflightChart
+                  period={this.state.timePeriod}
+                  chartData={this.props.inflightChartData}
+                />
               </CardBody>
             </Card>
           </StackItem>
diff --git a/console/react/src/overview/dashboard/dashboardPage.test.js b/console/react/src/overview/dashboard/dashboardPage.test.js
index 3269f7d..535ae9f 100644
--- a/console/react/src/overview/dashboard/dashboardPage.test.js
+++ b/console/react/src/overview/dashboard/dashboardPage.test.js
@@ -1,15 +1,41 @@
+/*
+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.
+*/
+
 import React from "react";
-import { render } from '@testing-library/react';
+import { render } from "@testing-library/react";
+import { service, login, TEST_PORT } from "../../serviceTest";
+import throughputChartData from "./throughputData";
+import inflightChartData from "./inflightData";
 import DashboardPage from "./dashboardPage";
-import { mockService } from "../../qdrService.mock";
 
-it("renders the DashboardPage component", () => {
+it("renders the DashboardPage component", async () => {
   const props = {
-    service: mockService({})
-  }
-  const { getByLabelText } = render(
-    <DashboardPage {...props} />
-  )
+    service,
+    throughputChartData: new throughputChartData(service),
+    inflightChartData: new inflightChartData(service)
+  };
+
+  if (!TEST_PORT) console.log("using mock service");
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
+
+  const { getByLabelText } = render(<DashboardPage {...props} />);
 
   // make sure it rendered the component
   expect(getByLabelText("dashboard-page")).toBeInTheDocument();
diff --git a/console/react/src/overview/dashboard/delayedDeliveriesCard.js b/console/react/src/overview/dashboard/delayedDeliveriesCard.js
index 0fa44b3..990d029 100644
--- a/console/react/src/overview/dashboard/delayedDeliveriesCard.js
+++ b/console/react/src/overview/dashboard/delayedDeliveriesCard.js
@@ -1,9 +1,27 @@
+/*
+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.
+*/
+
 import React from "react";
 import { Table, TableHeader, TableBody } from "@patternfly/react-table";
-import ConnectionClose from "../../connectionClose";
+import ConnectionClose from "../../common/connectionClose";
 
-// update the table every 5 seconds
-const UPDATE_INTERVAL = 1000 * 5;
+const UPDATE_INTERVAL = 5000;
 
 class DelayedDeliveriesCard extends React.Component {
   constructor(props) {
@@ -25,13 +43,21 @@ class DelayedDeliveriesCard extends React.Component {
   }
 
   closeButton = (value, extraInfo) => {
-    return <ConnectionClose extraInfo={extraInfo} {...this.props} service={this.props.service} />;
+    return (
+      <ConnectionClose
+        extraInfo={extraInfo}
+        {...this.props}
+        service={this.props.service}
+      />
+    );
   };
 
   componentDidMount = () => {
     this.mounted = true;
     this.timer = setInterval(this.updateData, UPDATE_INTERVAL);
     this.updateData();
+    // we need 2 measurements to get a rate
+    setTimeout(this.updateData, 1000);
   };
 
   componentWillUnmount = () => {
@@ -50,7 +76,10 @@ class DelayedDeliveriesCard extends React.Component {
           let response = nodes[node]["router.link"];
           // eslint-disable-next-line no-loop-func
           response.results.forEach(result => {
-            let link = this.props.service.utilities.flatten(response.attributeNames, result);
+            let link = this.props.service.utilities.flatten(
+              response.attributeNames,
+              result
+            );
             if (link.linkType === "endpoint") {
               link.router = this.props.service.utilities.nameFromId(node);
               link.role = "normal";
@@ -71,10 +100,16 @@ class DelayedDeliveriesCard extends React.Component {
                 ["deliveriesDelayed1Sec", "deliveriesDelayed10Sec"],
                 this.rates,
                 link.name,
-                12 // average over 12 snapshots (each snapshot is 5 seconds apart)
+                2 // average over 2 snapshots (each snapshot is 5 seconds apart)
+              );
+              link.deliveriesDelayed1SecRate = Math.round(
+                delayedRates.deliveriesDelayed1Sec,
+                1
+              );
+              link.deliveriesDelayed10SecRate = Math.round(
+                delayedRates.deliveriesDelayed10Sec,
+                1
               );
-              link.deliveriesDelayed1SecRate = Math.round(delayedRates.deliveriesDelayed1Sec, 1);
-              link.deliveriesDelayed10SecRate = Math.round(delayedRates.deliveriesDelayed10Sec, 1);
               /* The killConnection event handler (in qdrOverview.js) expects
                  a row object with a routerId and the identity of a connection. 
                  Here we set those attributes so that when killConnection is 
@@ -90,7 +125,9 @@ class DelayedDeliveriesCard extends React.Component {
         if (links.length === 0) return;
         // update the grid's data
         links = links.filter(link => {
-          return link.deliveriesDelayed1SecRate > 0 || link.deliveriesDelayed10SecRate > 0;
+          return (
+            link.deliveriesDelayed1SecRate > 0 || link.deliveriesDelayed10SecRate > 0
+          );
         });
         links.sort((a, b) => {
           if (a.deliveriesDelayed1SecRate > b.deliveriesDelayed1SecRate) return -1;
diff --git a/console/react/src/overview/dashboard/inflightChart.test.js b/console/react/src/overview/dashboard/inflightChart.test.js
index ce2177c..d90e5c3 100644
--- a/console/react/src/overview/dashboard/inflightChart.test.js
+++ b/console/react/src/overview/dashboard/inflightChart.test.js
@@ -1,15 +1,39 @@
+/*
+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.
+*/
+
 import React from "react";
-import { render } from '@testing-library/react';
+import { render } from "@testing-library/react";
+import { service, login, TEST_PORT } from "../../serviceTest";
 import InflightChart from "./inflightChart";
-import { mockService } from "../../qdrService.mock";
+import InflightChartData from "./inflightData";
 
-it("renders the InflightChart component", () => {
+it("renders the InflightChart component", async () => {
   const props = {
-    service: mockService({})
-  }
-  const { getByLabelText } = render(
-    <InflightChart {...props} />
-  )
+    service,
+    chartData: new InflightChartData(service)
+  };
+
+  if (!TEST_PORT) console.log("using mock service");
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
+
+  const { getByLabelText } = render(<InflightChart {...props} />);
 
   // make sure it rendered the component
   setTimeout(() => expect(getByLabelText("inflight-chart")).toBeInTheDocument(), 1);
diff --git a/console/react/src/overview/dashboard/inflightData.js b/console/react/src/overview/dashboard/inflightData.js
new file mode 100644
index 0000000..50a8dbb
--- /dev/null
+++ b/console/react/src/overview/dashboard/inflightData.js
@@ -0,0 +1,61 @@
+/*
+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.
+*/
+
+import ChartData from "./chartData";
+
+class InflightData extends ChartData {
+  constructor(service) {
+    super(service);
+    this.isRate = false;
+    this.running = true;
+  }
+
+  stop = () => {
+    this.running = false;
+  };
+  updateData = () => {
+    if (!this.service.management.connection.is_connected()) return;
+    this.service.management.topology.fetchAllEntities(
+      {
+        entity: "router.link",
+        attrs: ["undeliveredCount", "unsettledCount", "linkType", "linkDir"]
+      },
+      results => {
+        if (!this.running) return;
+        let inflight = 0;
+        for (let id in results) {
+          const aresult = results[id]["router.link"];
+          for (let i = 0; i < aresult.results.length; i++) {
+            const result = this.service.utilities.flatten(
+              aresult.attributeNames,
+              aresult.results[i]
+            );
+            inflight +=
+              result.linkType === "endpoint" && result.linkDir === "out"
+                ? parseInt(result.unsettledCount) + parseInt(result.undeliveredCount)
+                : 0;
+          }
+        }
+        this.addData(inflight);
+      }
+    );
+  };
+}
+
+export default InflightData;
diff --git a/console/react/src/layout.js b/console/react/src/overview/dashboard/layout.js
similarity index 65%
rename from console/react/src/layout.js
rename to console/react/src/overview/dashboard/layout.js
index 4bd80e7..46fe58c 100644
--- a/console/react/src/layout.js
+++ b/console/react/src/overview/dashboard/layout.js
@@ -36,38 +36,41 @@ import {
   PageSidebar
 } from "@patternfly/react-core";
 
-import { BrowserRouter as Router, Switch, Route, Link, Redirect } from "react-router-dom";
+import { HashRouter as Router, Switch, Route, Link, Redirect } from "react-router-dom";
 
 import accessibleStyles from "@patternfly/patternfly/utilities/Accessibility/accessibility.css";
 import { css } from "@patternfly/react-styles";
 import { PowerOffIcon } from "@patternfly/react-icons";
-import DropdownMenu from "./DropdownMenu";
-import ConnectPage from "./connectPage";
-import DashboardPage from "./overview/dashboard/dashboardPage";
-import OverviewPage from "./overview/overviewPage";
-import DetailsTablePage from "./detailsTablePage";
-import EntitiesPage from "./details/enitiesPage";
-import TopologyPage from "./topology/topologyPage";
-import MessageFlowPage from "./chord/chordPage";
-import SchemaPage from "./details/schema/schemaPage";
-import LogDetails from "./overview/logDetails";
-import ConnectForm from "./connect-form";
+import DropdownMenu from "../../common/DropdownMenu";
+import ConnectPage from "../../connect/connectPage";
+import DashboardPage from "./dashboardPage";
+import OverviewPage from "../overviewPage";
+import DetailsTablePage from "../../details/detailsTablePage";
+import EntitiesPage from "../../details/entitiesPage";
+import TopologyPage from "../../topology/topologyPage";
+import MessageFlowPage from "../../chord/chordPage";
+import SchemaPage from "../../details/schema/schemaPage";
+import LogDetails from "../logDetails";
+import ConnectForm from "../../connect/connect-form";
 import NotificationDrawer from "./notificationDrawer";
-import { utils } from "./amqp/utilities";
+import { utils } from "../../common/amqp/utilities";
+import throughputData from "./throughputData";
+import inflightData from "./inflightData";
 
 class PageLayout extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
       connected: false,
-      connectPath: "",
+      connecting: false,
+      isConnectFormOpen: false,
       activeGroup: "overview",
       activeItem: "dashboard",
       isNavOpenDesktop: true,
       isNavOpenMobile: false,
       isMobileView: false,
       user: "anonymous",
-      isConnectFormOpen: false
+      timePeriod: 60
     };
     this.isDropdownOpen = false;
 
@@ -85,23 +88,84 @@ class PageLayout extends React.Component {
       visualizations: [{ name: "topology" }, { name: "flow", title: "Message flow" }],
       details: [{ name: "entities" }, { name: "schema" }]
     };
+    this.state.connecting = true;
+    this.tryInitialConnect();
   }
 
+  componentDidMount = () => {
+    this.chartTimer = setInterval(this.updateCharts, 1000);
+    this.throughputChartData = new throughputData(this.service);
+    this.inflightChartData = new inflightData(this.service);
+    this.updateCharts();
+  };
+
+  componentWillUnmount = () => {
+    if (this.chartTimer) {
+      this.throughputChartData.stop();
+      this.inflightChartData.stop();
+      clearInterval(this.chartTimer);
+    }
+  };
+
+  tryInitialConnect = () => {
+    const connectOptions = {
+      address: window.location.hostname,
+      port: window.location.port,
+      timeout: 2000,
+      reconnect: false
+    };
+    this.service.connect(connectOptions).then(
+      () => {
+        this.service.setReconnect(true);
+        this.schema = this.service.schema;
+        this.props.history.replace("/dashboard");
+        this.redirect = true;
+        this.setState({ connecting: false, connected: true });
+      },
+      () => {
+        //this.service.disconnect();
+        this.props.history.replace("/");
+        this.setState({ connecting: false });
+      }
+    );
+  };
+
   // the connection to the routers was lost
   setLocation = whatHappened => {
     if (whatHappened === "disconnect") {
+      this.handleAddNotification(
+        "event",
+        "Connection to router dropped",
+        new Date(),
+        "warning"
+      );
+      this.lastLocation = this.props.location.pathname;
       this.setState({ connected: false });
     } else if (whatHappened === "reconnect") {
-      this.handleConnectCancel();
-      const connectPath = this.lastConnectPath || "/";
-      this.lastConnectPath = connectPath;
+      this.handleAddNotification(
+        "event",
+        "Connection to router resumed",
+        new Date(),
+        "info"
+      );
+      this.redirect = true;
+      let to = "/dashboard";
+      if (this.lastLocation) {
+        to = this.lastLocation;
+      }
+      this.props.history.push(to);
       this.setState({
-        connectPath,
+        isConnectFormOpen: false,
         connected: true
       });
     }
   };
 
+  updateCharts = () => {
+    this.throughputChartData.updateData();
+    this.inflightChartData.updateData();
+  };
+
   onDropdownToggle = () => {
     this.isDropdownOpen = !this.isDropdownOpen;
     this.dropdownRef.show(this.isDropdownOpen);
@@ -118,7 +182,7 @@ class PageLayout extends React.Component {
 
   handleConnect = (connectPath, result) => {
     if (this.state.connected) {
-      this.setState({ connectPath: "", connected: false }, () => {
+      this.setState({ connected: false }, () => {
         this.handleConnectCancel();
         this.service.disconnect();
         this.handleAddNotification("event", "Manually disconnected", new Date(), "info");
@@ -135,24 +199,29 @@ class PageLayout extends React.Component {
           break;
         }
       }
-      this.handleConnectCancel();
-      this.handleAddNotification("event", `Console connected to router`, new Date(), "success");
+      this.handleAddNotification(
+        "event",
+        `Console connected to router`,
+        new Date(),
+        "success",
+        true
+      );
 
+      this.redirect = true;
+      this.props.history.replace(connectPath);
       this.setState({
         activeItem,
         activeGroup,
         connected: true,
-        connectPath
+        isConnectFormOpen: false
       });
     }
   };
 
-  onNavSelect = (result, connectPath) => {
-    this.lastConnectPath = connectPath || this.state.connectPath;
+  onNavSelect = result => {
     this.setState({
       activeItem: result.itemId,
-      activeGroup: result.groupId,
-      connectPath: ""
+      activeGroup: result.groupId
     });
   };
 
@@ -191,13 +260,14 @@ class PageLayout extends React.Component {
     return this.state.connected;
   };
 
-  handleAddNotification = (section, message, timestamp, severity) => {
+  handleAddNotification = (section, message, timestamp, severity, silent) => {
     if (this.notificationRef) {
       this.notificationRef.addNotification({
         section,
         message,
         timestamp,
-        severity
+        severity,
+        silent
       });
     }
   };
@@ -222,7 +292,12 @@ class PageLayout extends React.Component {
                 {this.nav[section].map(item => {
                   const key = item.name;
                   return (
-                    <NavItem groupId={section} itemId={key} isActive={activeItem === key} key={key}>
+                    <NavItem
+                      groupId={section}
+                      itemId={key}
+                      isActive={activeItem === key}
+                      key={key}
+                    >
                       <Link to={`/${item.pre ? section + "/" : ""}${key}`}>
                         {item.title ? item.title : utils.Icap(key)}
                       </Link>
@@ -237,7 +312,9 @@ class PageLayout extends React.Component {
     );
     const PageToolbar = (
       <Toolbar>
-        <ToolbarGroup className={css(accessibleStyles.screenReader, accessibleStyles.visibleOnLg)}>
+        <ToolbarGroup
+          className={css(accessibleStyles.screenReader, accessibleStyles.visibleOnLg)}
+        >
           <ToolbarItem>
             <Button
               id="connectButton"
@@ -253,7 +330,9 @@ class PageLayout extends React.Component {
           </ToolbarItem>
         </ToolbarGroup>
         <ToolbarGroup>
-          <ToolbarItem className={css(accessibleStyles.screenReader, accessibleStyles.visibleOnMd)}>
+          <ToolbarItem
+            className={css(accessibleStyles.screenReader, accessibleStyles.visibleOnMd)}
+          >
             <DropdownToggle className="user-button" onToggle={this.onDropdownToggle}>
               {this.state.user}
             </DropdownToggle>
@@ -281,7 +360,9 @@ class PageLayout extends React.Component {
       />
     );
     const pageId = "main-content-page-layout-manual-nav";
-    const PageSkipToContent = <SkipToContent href={`#${pageId}`}>Skip to Content</SkipToContent>;
+    const PageSkipToContent = (
+      <SkipToContent href={`#${pageId}`}>Skip to Content</SkipToContent>
+    );
 
     const sidebar = PageNav => {
       if (this.state.connected) {
@@ -312,26 +393,19 @@ class PageLayout extends React.Component {
               {...more}
             />
           ) : (
-            <Redirect to={{ pathname: "/login", state: { from: props.location } }} />
+            <Redirect
+              to={{
+                pathname: `/login${this.state.connecting ? "/connecting" : ""}`,
+                state: { from: props.location }
+              }}
+            />
           )
         }
       />
     );
 
-    // When we need to display a different component(page),
-    // we render a <Redirect> object
-    const redirectAfterConnect = () => {
-      let { connectPath } = this.state;
-      if (connectPath !== "") {
-        if (connectPath === "/login") connectPath = "/";
-        this.lastConnectPath = connectPath;
-        return <Redirect to={connectPath} />;
-      }
-      return <React.Fragment />;
-    };
-
     const connectForm = () => {
-      return (
+      return this.state.isConnectFormOpen ? (
         <ConnectForm
           service={this.service}
           isConnectFormOpen={this.state.isConnectFormOpen}
@@ -341,9 +415,22 @@ class PageLayout extends React.Component {
           isConnected={this.state.connected}
           fromLayout={true}
         />
+      ) : (
+        <React.Fragment />
       );
     };
 
+    // When we need to display a different component(page),
+    // we render a <Redirect> object
+    const redirectAfterConnect = () => {
+      if (this.state.connected && this.redirect) {
+        this.redirect = false;
+        return <Redirect to={this.props.location.pathname} />;
+      } else {
+        return <React.Fragment />;
+      }
+    };
+
     return (
       <Router>
         {redirectAfterConnect()}
@@ -356,10 +443,25 @@ class PageLayout extends React.Component {
         >
           {connectForm()}
           <Switch>
-            <PrivateRoute path="/" exact component={DashboardPage} />
-            <PrivateRoute path="/dashboard" component={DashboardPage} />
+            <PrivateRoute
+              path="/"
+              exact
+              throughputChartData={this.throughputChartData}
+              inflightChartData={this.inflightChartData}
+              component={DashboardPage}
+            />
+            <PrivateRoute
+              path="/dashboard"
+              throughputChartData={this.throughputChartData}
+              inflightChartData={this.inflightChartData}
+              component={DashboardPage}
+            />
             <PrivateRoute path="/overview/:entity" component={OverviewPage} />
-            <PrivateRoute path="/details" schema={this.schema} component={DetailsTablePage} />
+            <PrivateRoute
+              path="/details"
+              schema={this.schema}
+              component={DetailsTablePage}
+            />
             <PrivateRoute path="/topology" component={TopologyPage} />
             <PrivateRoute path="/flow" component={MessageFlowPage} />
             <PrivateRoute path="/logs" component={LogDetails} />
@@ -370,6 +472,15 @@ class PageLayout extends React.Component {
               render={props => (
                 <ConnectPage
                   {...props}
+                  connecting={this.state.connecting}
+                  connectingTitle={
+                    this.state.connecting ? "Attempting to auto connect" : undefined
+                  }
+                  connectingMessage={
+                    this.state.connecting
+                      ? `Trying to connect to ${window.location.hostname}:${window.location.port}`
+                      : undefined
+                  }
                   fromPath={"/"}
                   service={this.service}
                   config={this.props.config}
diff --git a/console/react/src/overview/dashboard/layout.test.js b/console/react/src/overview/dashboard/layout.test.js
new file mode 100644
index 0000000..b8edfb1
--- /dev/null
+++ b/console/react/src/overview/dashboard/layout.test.js
@@ -0,0 +1,76 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render } from "@testing-library/react";
+import { service } from "../../serviceTest";
+import PageLayout from "./layout";
+
+it("renders the correct title", async () => {
+  const title = "Test Layout Page";
+  const props = {
+    config: { title: title },
+    service,
+    history: { push: () => {}, replace: () => {} },
+    location: { pathname: "/dashboard" }
+  };
+
+  const { getAllByText } = render(<PageLayout {...props} />);
+
+  // the correct title should be found
+  expect(getAllByText(title));
+});
+
+it("calls some utility functions", () => {
+  let name = service.utilities.clientName({});
+  expect(name).toEqual("client");
+
+  name = service.utilities.clientName({ container: "test-container" });
+  expect(name).toEqual("test-container");
+
+  name = service.utilities.clientName({ properties: { product: "test-product" } });
+  expect(name).toEqual("test-product");
+
+  let addr = service.utilities.addr_class();
+  expect(addr).toEqual("-");
+  addr = service.utilities.addr_class("Mtest");
+  expect(addr).toEqual("mobile");
+  addr = service.utilities.addr_class("Atest");
+  expect(addr).toEqual("area");
+  addr = service.utilities.addr_class("Rtest");
+  expect(addr).toEqual("router");
+  addr = service.utilities.addr_class("Ltest");
+  expect(addr).toEqual("local");
+  addr = service.utilities.addr_class("Htest");
+  expect(addr).toEqual("edge");
+  addr = service.utilities.addr_class("Ztest");
+  expect(addr).toEqual("unknown: Z");
+
+  let sec = service.utilities.connSecurity({});
+  expect(sec).toEqual("no-security");
+  sec = service.utilities.connSecurity({ isEncrypted: true, sasl: "GSSAPI" });
+  expect(sec).toEqual("Kerberos");
+  sec = service.utilities.connSecurity({
+    isEncrypted: true,
+    sasl: "",
+    sslProto: "https",
+    sslCipher: "test"
+  });
+  expect(sec).toEqual("https(test)");
+});
diff --git a/console/react/src/notificationDrawer.js b/console/react/src/overview/dashboard/notificationDrawer.js
similarity index 91%
rename from console/react/src/notificationDrawer.js
rename to console/react/src/overview/dashboard/notificationDrawer.js
index 9a947c5..ccd6cf5 100644
--- a/console/react/src/notificationDrawer.js
+++ b/console/react/src/overview/dashboard/notificationDrawer.js
@@ -34,13 +34,13 @@ import {
   TimesIcon
 } from "@patternfly/react-icons";
 import AlertList from "./alertList";
-import { safePlural } from "./qdrGlobals";
+import { safePlural } from "../../common/qdrGlobals";
 
 class NotificationDrawer extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
-      isShown: false,  // is the drawer shown
+      isShown: false, // is the drawer shown
       expanded: false, // is the drawer wide
       isAnyUnread: false,
       accordionSections: {
@@ -78,7 +78,7 @@ class NotificationDrawer extends React.Component {
     }
   };
 
-  addNotification = ({ section, message, timestamp, severity }) => {
+  addNotification = ({ section, message, timestamp, severity, silent }) => {
     const { accordionSections } = this.state;
     const event = { message, timestamp, severity };
     event.date = timestamp.toLocaleDateString(undefined, {
@@ -93,7 +93,7 @@ class NotificationDrawer extends React.Component {
     });
     event.isRead = false;
     accordionSections[section].events.unshift(event);
-    if (this.alertListRef) {
+    if (this.alertListRef && !silent) {
       this.alertListRef.addAlert(severity, message);
     }
     this.setState({
@@ -124,8 +124,7 @@ class NotificationDrawer extends React.Component {
     let isAnyUnread = false;
     for (let sectionKey in accordionSections) {
       isAnyUnread =
-        isAnyUnread ||
-        accordionSections[sectionKey].events.some(e => !e.isRead);
+        isAnyUnread || accordionSections[sectionKey].events.some(e => !e.isRead);
     }
     this.setState({ accordionSections, isAnyUnread });
   };
@@ -156,11 +155,7 @@ class NotificationDrawer extends React.Component {
     const DrawerTitle = (
       <div className="drawer-pf-title">
         <Button variant="plain" aria-label="expand" onClick={this.toggleExpand}>
-          {this.state.expanded ? (
-            <AngleDoubleRightIcon />
-          ) : (
-              <AngleDoubleLeftIcon />
-            )}
+          {this.state.expanded ? <AngleDoubleRightIcon /> : <AngleDoubleLeftIcon />}
         </Button>
         <h3 className="text-center">Notifications Drawer</h3>
         <Button variant="plain" aria-label="close" onClick={this.close}>
@@ -202,7 +197,10 @@ class NotificationDrawer extends React.Component {
               {Object.keys(this.state.accordionSections).map(sectionKey => {
                 const section = this.state.accordionSections[sectionKey];
                 return (
-                  <AccordionItem aria-label="notification-item" key={`${sectionKey}-item`}>
+                  <AccordionItem
+                    aria-label="notification-item"
+                    key={`${sectionKey}-item`}
+                  >
                     <AccordionToggle
                       onClick={() => this.toggleDrawer(sectionKey)}
                       isExpanded={section.isOpen}
@@ -212,10 +210,10 @@ class NotificationDrawer extends React.Component {
                       {section.title}
                       <span className="panel-counter">{`${
                         section.events.filter(e => !e.isRead).length
-                        } new ${safePlural(
-                          section.events.filter(e => !e.isRead).length,
-                          "event"
-                        )}`}</span>
+                      } new ${safePlural(
+                        section.events.filter(e => !e.isRead).length,
+                        "event"
+                      )}`}</span>
                     </AccordionToggle>
                     <AccordionContent
                       key={`${sectionKey}-content`}
@@ -225,7 +223,7 @@ class NotificationDrawer extends React.Component {
                       <div
                         className={`panel-body ${
                           section.events.length === 0 ? "hidden" : ""
-                          }`}
+                        }`}
                       >
                         {section.events.map((event, i) => {
                           return (
@@ -233,7 +231,7 @@ class NotificationDrawer extends React.Component {
                               key={`${sectionKey}-event-${i}`}
                               className={`drawer-pf-notification ${
                                 event.isRead ? "" : "unread"
-                                }`}
+                              }`}
                               onClick={() => this.markAsRead(event)}
                             >
                               {severityIcon(event)}
@@ -253,7 +251,7 @@ class NotificationDrawer extends React.Component {
                       <div
                         className={`blank-slate-pf ${
                           section.events.length === 0 ? "" : "hidden"
-                          }`}
+                        }`}
                       >
                         <div className="blank-slate-pf-icon">
                           <span className="pficon pficon-info"></span>
@@ -263,13 +261,13 @@ class NotificationDrawer extends React.Component {
                       <div
                         className={`drawer-pf-action ${
                           section.events.length > 0 ? "" : "hidden"
-                          }`}
+                        }`}
                       >
                         <div className="drawer-pf-action-link">
                           <button
                             className={`btn btn-link ${
                               this.hasUnread(section) ? "" : "disabled"
-                              }`}
+                            }`}
                             onClick={() => this.markAllRead(sectionKey)}
                           >
                             Mark All Read
diff --git a/console/react/src/overview/dashboard/notificationDrawer.test.js b/console/react/src/overview/dashboard/notificationDrawer.test.js
new file mode 100644
index 0000000..ecd540b
--- /dev/null
+++ b/console/react/src/overview/dashboard/notificationDrawer.test.js
@@ -0,0 +1,50 @@
+/*
+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.
+*/
+
+import React from "react";
+import { render, fireEvent } from "@testing-library/react";
+import NotificationDrawer from "./notificationDrawer";
+
+it("renders without crashing", () => {
+  render(<NotificationDrawer />);
+});
+
+it("renders a notification icon", () => {
+  let notificationRef = null;
+  const { getByLabelText, getByText } = render(
+    <NotificationDrawer ref={el => (notificationRef = el)} />
+  );
+
+  // the notifications icon should be present
+  const notificationIcon = getByLabelText("Notifications");
+  expect(notificationIcon).toBeInTheDocument();
+
+  // add a notification
+  const section = "action";
+  const message = "test message";
+  const timestamp = new Date();
+  const severity = "info";
+  notificationRef.addNotification({ section, message, timestamp, severity });
+
+  // click the notification icon
+  fireEvent.click(notificationIcon);
+
+  // there should now be a single notification-item
+  expect(getByText("1 new event")).toBeInTheDocument();
+});
diff --git a/console/react/src/overview/dashboard/throughputChart.js b/console/react/src/overview/dashboard/throughputChart.js
index 291e75e..6600df4 100644
--- a/console/react/src/overview/dashboard/throughputChart.js
+++ b/console/react/src/overview/dashboard/throughputChart.js
@@ -25,26 +25,8 @@ class ThroughputChart extends ChartBase {
     this.title = "Deliveries per sec";
     this.color = "#99C2EB"; //ChartThemeColor.blue;
     this.setStyle(this.color);
-    this.isRate = true;
     this.ariaLabel = "throughput-chart";
   }
-
-  updateData = () => {
-    this.props.service.management.topology.fetchAllEntities(
-      {
-        entity: "router",
-        attrs: ["deliveriesEgress"]
-      },
-      results => {
-        let deliveries = 0;
-        for (let id in results) {
-          const aresult = results[id]["router"];
-          deliveries += parseInt(aresult.results[0]);
-        }
-        this.addData(deliveries);
-      }
-    );
-  };
 }
 
 export default ThroughputChart;
diff --git a/console/react/src/overview/dashboard/throughputChart.test.js b/console/react/src/overview/dashboard/throughputChart.test.js
index e74f9b1..2d0c41d 100644
--- a/console/react/src/overview/dashboard/throughputChart.test.js
+++ b/console/react/src/overview/dashboard/throughputChart.test.js
@@ -1,15 +1,39 @@
+/*
+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.
+*/
+
 import React from "react";
-import { render } from '@testing-library/react';
+import { render } from "@testing-library/react";
+import { service, login, TEST_PORT } from "../../serviceTest";
+import ThroughputChartData from "./throughputData";
 import ThroughputChart from "./throughputChart";
-import { mockService } from "../../qdrService.mock";
 
-it("renders the ThroughputChart component", () => {
+it("renders the ThroughputChart component", async () => {
   const props = {
-    service: mockService({})
-  }
-  const { getByLabelText, queryByLabelText } = render(
-    <ThroughputChart {...props} />
-  )
+    service,
+    chartData: new ThroughputChartData(service)
+  };
+
+  if (!TEST_PORT) console.log("using mock service");
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
+
+  const { getByLabelText, queryByLabelText } = render(<ThroughputChart {...props} />);
 
   // the component should initially render
   // blank until it gets the contianer's width.
diff --git a/console/react/src/overview/dashboard/throughputChart.js b/console/react/src/overview/dashboard/throughputData.js
similarity index 74%
copy from console/react/src/overview/dashboard/throughputChart.js
copy to console/react/src/overview/dashboard/throughputData.js
index 291e75e..4c02d9b 100644
--- a/console/react/src/overview/dashboard/throughputChart.js
+++ b/console/react/src/overview/dashboard/throughputData.js
@@ -17,25 +17,28 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import ChartBase from "./chartBase";
+import ChartData from "./chartData";
 
-class ThroughputChart extends ChartBase {
-  constructor(props) {
-    super(props);
-    this.title = "Deliveries per sec";
-    this.color = "#99C2EB"; //ChartThemeColor.blue;
-    this.setStyle(this.color);
+class ThroughputData extends ChartData {
+  constructor(service) {
+    super(service);
     this.isRate = true;
-    this.ariaLabel = "throughput-chart";
+    this.running = true;
   }
 
+  stop = () => {
+    this.running = false;
+  };
+
   updateData = () => {
-    this.props.service.management.topology.fetchAllEntities(
+    if (!this.service.management.connection.is_connected()) return;
+    this.service.management.topology.fetchAllEntities(
       {
         entity: "router",
         attrs: ["deliveriesEgress"]
       },
       results => {
+        if (!this.running) return;
         let deliveries = 0;
         for (let id in results) {
           const aresult = results[id]["router"];
@@ -47,4 +50,4 @@ class ThroughputChart extends ChartBase {
   };
 }
 
-export default ThroughputChart;
+export default ThroughputData;
diff --git a/console/react/src/overview/dataSources/connectionData.js b/console/react/src/overview/dataSources/connectionData.js
index 4942c7b..e5225cf 100644
--- a/console/react/src/overview/dataSources/connectionData.js
+++ b/console/react/src/overview/dataSources/connectionData.js
@@ -17,7 +17,7 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import ConnectionClose from "../../connectionClose";
+import ConnectionClose from "../../common/connectionClose";
 
 class ConnectionData {
   constructor(service) {
@@ -56,10 +56,7 @@ class ConnectionData {
           const result = record.results.find(
             r => r[identityIndex] === currentRecord.identity
           );
-          let connection = this.service.utilities.flatten(
-            record.attributeNames,
-            result
-          );
+          let connection = this.service.utilities.flatten(record.attributeNames, result);
           connection = this.service.utilities.formatAttributes(
             connection,
             schema.entityTypes["connection"]
@@ -100,8 +97,7 @@ class ConnectionData {
               let sec = "no-security";
               if (connection.isEncrypted) {
                 if (sasl === "GSSAPI") sec = "Kerberos";
-                else
-                  sec = connection.sslProto + "(" + connection.sslCipher + ")";
+                else sec = connection.sslProto + "(" + connection.sslCipher + ")";
               }
 
               let host = connection.host;
diff --git a/console/react/src/overview/overviewPage.js b/console/react/src/overview/overviewPage.js
index 023e37d..5268222 100644
--- a/console/react/src/overview/overviewPage.js
+++ b/console/react/src/overview/overviewPage.js
@@ -29,7 +29,7 @@ import {
 import { Card, CardBody } from "@patternfly/react-core";
 
 import OverviewTable from "./overviewTable";
-import Updated from "../updated";
+import Updated from "../common/updated";
 
 class OverviewPage extends React.Component {
   constructor(props) {
diff --git a/console/react/src/overview/overviewPage.test.js b/console/react/src/overview/overviewPage.test.js
index 6edafb9..bc2d0e8 100644
--- a/console/react/src/overview/overviewPage.test.js
+++ b/console/react/src/overview/overviewPage.test.js
@@ -1,18 +1,87 @@
+/*
+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.
+*/
+
 import React from "react";
-import { render } from '@testing-library/react';
-import { mockService } from "../qdrService.mock";
+import { render } from "@testing-library/react";
+import { service, login, TEST_PORT } from "../serviceTest";
 import OverviewPage from "./overviewPage";
 
-it("renders the overview page", () => {
+it("renders the overview connections page", async () => {
+  const entity = "connections";
+  const props = {
+    service,
+    location: { pathname: `/overview/${entity}` },
+    lastUpdated: () => {}
+  };
+
+  if (!TEST_PORT) console.log("using mock service");
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
+
+  const { getByLabelText } = render(<OverviewPage {...props} />);
+  expect(getByLabelText(entity)).toBeInTheDocument();
+});
+
+it("renders the overview logs page", async () => {
   const entity = "logs";
   const props = {
-    service: mockService({}),
+    service,
+    location: { pathname: `/overview/${entity}` },
+    lastUpdated: () => {}
+  };
+
+  const { getByLabelText } = render(<OverviewPage {...props} />);
+  expect(getByLabelText(entity)).toBeInTheDocument();
+});
+
+it("renders the overview links page", async () => {
+  const entity = "links";
+  const props = {
+    service,
+    location: { pathname: `/overview/${entity}` },
+    lastUpdated: () => {}
+  };
+
+  const { getByLabelText } = render(<OverviewPage {...props} />);
+  expect(getByLabelText(entity)).toBeInTheDocument();
+});
+
+it("renders the overview routers page", async () => {
+  const entity = "routers";
+  const props = {
+    service,
+    location: { pathname: `/overview/${entity}` },
+    lastUpdated: () => {}
+  };
+
+  const { getByLabelText } = render(<OverviewPage {...props} />);
+  expect(getByLabelText(entity)).toBeInTheDocument();
+});
+
+it("renders the overview addresses page", async () => {
+  const entity = "addresses";
+  const props = {
+    service,
     location: { pathname: `/overview/${entity}` },
-    lastUpdated: () => { }
-  }
+    lastUpdated: () => {}
+  };
 
-  const { getByTestId } = render(
-    <OverviewPage {...props} />
-  );
-  expect(getByTestId("overview-page")).toBeInTheDocument();
+  const { getByLabelText } = render(<OverviewPage {...props} />);
+  expect(getByLabelText(entity)).toBeInTheDocument();
 });
diff --git a/console/react/src/overview/overviewTable.js b/console/react/src/overview/overviewTable.js
index e412370..2832876 100644
--- a/console/react/src/overview/overviewTable.js
+++ b/console/react/src/overview/overviewTable.js
@@ -28,16 +28,16 @@ import {
 } from "@patternfly/react-table";
 import { Button, Pagination } from "@patternfly/react-core";
 import { Redirect } from "react-router-dom";
-import TableToolbar from "../tableToolbar";
+import TableToolbar from "../common/tableToolbar";
 import { dataMap } from "./entityData";
 
 // If the breadcrumb on the details page was used to return to this page,
 // we will have saved state info in props.location.state
 const propFromLocation = (props, which, defaultValue) =>
   props &&
-    props.location &&
-    props.location.state &&
-    typeof props.location.state[which] !== "undefined"
+  props.location &&
+  props.location.state &&
+  typeof props.location.state[which] !== "undefined"
     ? props.location.state[which]
     : defaultValue;
 
@@ -129,10 +129,7 @@ class OverviewTable extends React.Component {
 
   detailLink = (value, extraInfo) => {
     return (
-      <Button
-        className="link-button"
-        onClick={() => this.detailClick(value, extraInfo)}
-      >
+      <Button className="link-button" onClick={() => this.detailClick(value, extraInfo)}>
         {value}
       </Button>
     );
@@ -282,8 +279,7 @@ class OverviewTable extends React.Component {
       return (
         <Redirect
           to={{
-            pathname:
-              (this.dataSource && this.dataSource.detailPath) || "/details",
+            pathname: (this.dataSource && this.dataSource.detailPath) || "/details",
             state: this.state.redirectState
           }}
         />
diff --git a/console/react/src/overview/overviewTable.test.js b/console/react/src/overview/overviewTable.test.js
deleted file mode 100644
index d8e119e..0000000
--- a/console/react/src/overview/overviewTable.test.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import OverviewTable from "./overviewTable";
-import { mockService } from "../qdrService.mock";
-
-it("renders the OverviewTable component", () => {
-  const entity = "routers";
-  const props = {
-    service: mockService({}),
-    location: { pathname: `/overview/${entity}` },
-    lastUpdated: () => { }
-  }
-  const { getByLabelText } = render(
-    <OverviewTable {...props} />
-  )
-
-  // make sure it rendered the component
-  expect(getByLabelText(entity)).toBeInTheDocument();
-});
diff --git a/console/react/src/pleaseWait.js b/console/react/src/pleaseWait.js
deleted file mode 100644
index 228538b..0000000
--- a/console/react/src/pleaseWait.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import React from "react";
-import { TextContent, Text, TextVariants } from "@patternfly/react-core";
-import PropTypes from "prop-types";
-
-import { CogIcon } from "@patternfly/react-icons";
-
-class PleaseWait extends React.Component {
-  static propTypes = {
-    isOpen: PropTypes.bool.isRequired,
-    title: PropTypes.string.isRequired,
-    message: PropTypes.string.isRequired
-  };
-
-  state = {};
-
-  render() {
-    return (
-      this.props.isOpen && (
-        <div className="topic-creating-wrapper">
-          <div id="topicCogWrapper">
-            <CogIcon
-              id="topicCogMain"
-              className="spinning-clockwise"
-              color="#AAAAAA"
-            />
-            <CogIcon
-              id="topicCogUpper"
-              className="spinning-cclockwise"
-              color="#AAAAAA"
-            />
-            <CogIcon
-              id="topicCogLower"
-              className="spinning-cclockwise"
-              color="#AAAAAA"
-            />
-          </div>
-          <TextContent>
-            <Text component={TextVariants.h3}>{this.props.title}</Text>
-          </TextContent>
-          <TextContent>
-            <Text className="topic-creating-message" component={TextVariants.p}>
-              {this.props.message}
-            </Text>
-          </TextContent>
-        </div>
-      )
-    );
-  }
-}
-
-export default PleaseWait;
diff --git a/console/react/src/qdrPopup.js b/console/react/src/qdrPopup.js
deleted file mode 100644
index ba1ad28..0000000
--- a/console/react/src/qdrPopup.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from "react";
-
-class QDRPopup extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
-
-  render() {
-    return <div aria-label="popup" dangerouslySetInnerHTML={{ __html: this.props.content }} />;
-  }
-}
-
-export default QDRPopup;
diff --git a/console/react/src/qdrPopup.test.js b/console/react/src/qdrPopup.test.js
deleted file mode 100644
index c9c18c5..0000000
--- a/console/react/src/qdrPopup.test.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import QDRPopup from "./qdrPopup";
-
-it("the popup component renders HTML", () => {
-  const text = "Hello world";
-  const props = {
-    content: `<h1>${text}</h1>`
-  }
-  const { getByText, getByLabelText } = render(
-    <QDRPopup {...props} />
-  );
-  expect(getByLabelText("popup")).toBeInTheDocument()
-  expect(getByText("Hello world")).toBeInTheDocument();
-});
diff --git a/console/react/src/updated.js b/console/react/src/serviceTest.js
similarity index 64%
copy from console/react/src/updated.js
copy to console/react/src/serviceTest.js
index e762d8e..ef14533 100644
--- a/console/react/src/updated.js
+++ b/console/react/src/serviceTest.js
@@ -17,23 +17,12 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import React, { Component } from "react";
+import { mockService } from "../test_data/qdrService.mock";
+import { QDRService } from "./common/qdrService";
 
-class Updated extends Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
+const TEST_PORT = process.env.TEST_PORT;
+const service = TEST_PORT ? new QDRService() : mockService({});
 
-  render() {
-    return (
-      <pre aria-label="last-updated" data-pf-content="true" className="overview-loading">
-        {`Updated ${this.props.service.utilities.strDate(
-          this.props.lastUpdated
-        )}`}
-      </pre>
-    );
-  }
-}
-
-export default Updated;
+const login = () =>
+  service.connect({ address: "localhost", port: TEST_PORT, reconnect: false });
+export { service, TEST_PORT, login };
diff --git a/console/react/src/serviceWorker.js b/console/react/src/serviceWorker.js
deleted file mode 100644
index f8c7e50..0000000
--- a/console/react/src/serviceWorker.js
+++ /dev/null
@@ -1,135 +0,0 @@
-// This optional code is used to register a service worker.
-// register() is not called by default.
-
-// This lets the app load faster on subsequent visits in production, and gives
-// it offline capabilities. However, it also means that developers (and users)
-// will only see deployed updates on subsequent visits to a page, after all the
-// existing tabs open on the page have been closed, since previously cached
-// resources are updated in the background.
-
-// To learn more about the benefits of this model and instructions on how to
-// opt-in, read https://bit.ly/CRA-PWA
-
-const isLocalhost = Boolean(
-  window.location.hostname === 'localhost' ||
-    // [::1] is the IPv6 localhost address.
-    window.location.hostname === '[::1]' ||
-    // 127.0.0.1/8 is considered localhost for IPv4.
-    window.location.hostname.match(
-      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
-    )
-);
-
-export function register(config) {
-  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
-    // The URL constructor is available in all browsers that support SW.
-    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
-    if (publicUrl.origin !== window.location.origin) {
-      // Our service worker won't work if PUBLIC_URL is on a different origin
-      // from what our page is served on. This might happen if a CDN is used to
-      // serve assets; see https://github.com/facebook/create-react-app/issues/2374
-      return;
-    }
-
-    window.addEventListener('load', () => {
-      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
-
-      if (isLocalhost) {
-        // This is running on localhost. Let's check if a service worker still exists or not.
-        checkValidServiceWorker(swUrl, config);
-
-        // Add some additional logging to localhost, pointing developers to the
-        // service worker/PWA documentation.
-        navigator.serviceWorker.ready.then(() => {
-          console.log(
-            'This web app is being served cache-first by a service ' +
-              'worker. To learn more, visit https://bit.ly/CRA-PWA'
-          );
-        });
-      } else {
-        // Is not localhost. Just register service worker
-        registerValidSW(swUrl, config);
-      }
-    });
-  }
-}
-
-function registerValidSW(swUrl, config) {
-  navigator.serviceWorker
-    .register(swUrl)
-    .then(registration => {
-      registration.onupdatefound = () => {
-        const installingWorker = registration.installing;
-        if (installingWorker == null) {
-          return;
-        }
-        installingWorker.onstatechange = () => {
-          if (installingWorker.state === 'installed') {
-            if (navigator.serviceWorker.controller) {
-              // At this point, the updated precached content has been fetched,
-              // but the previous service worker will still serve the older
-              // content until all client tabs are closed.
-              console.log(
-                'New content is available and will be used when all ' +
-                  'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
-              );
-
-              // Execute callback
-              if (config && config.onUpdate) {
-                config.onUpdate(registration);
-              }
-            } else {
-              // At this point, everything has been precached.
-              // It's the perfect time to display a
-              // "Content is cached for offline use." message.
-              console.log('Content is cached for offline use.');
-
-              // Execute callback
-              if (config && config.onSuccess) {
-                config.onSuccess(registration);
-              }
-            }
-          }
-        };
-      };
-    })
-    .catch(error => {
-      console.error('Error during service worker registration:', error);
-    });
-}
-
-function checkValidServiceWorker(swUrl, config) {
-  // Check if the service worker can be found. If it can't reload the page.
-  fetch(swUrl)
-    .then(response => {
-      // Ensure service worker exists, and that we really are getting a JS file.
-      const contentType = response.headers.get('content-type');
-      if (
-        response.status === 404 ||
-        (contentType != null && contentType.indexOf('javascript') === -1)
-      ) {
-        // No service worker found. Probably a different app. Reload the page.
-        navigator.serviceWorker.ready.then(registration => {
-          registration.unregister().then(() => {
-            window.location.reload();
-          });
-        });
-      } else {
-        // Service worker found. Proceed as normal.
-        registerValidSW(swUrl, config);
-      }
-    })
-    .catch(() => {
-      console.log(
-        'No internet connection found. App is running in offline mode.'
-      );
-    });
-}
-
-export function unregister() {
-  if ('serviceWorker' in navigator) {
-    navigator.serviceWorker.ready.then(registration => {
-      registration.unregister();
-    });
-  }
-}
diff --git a/console/react/src/setupTests.js b/console/react/src/setupTests.js
index a8be446..f3021dc 100644
--- a/console/react/src/setupTests.js
+++ b/console/react/src/setupTests.js
@@ -1,3 +1,23 @@
+/*
+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.
+*/
+
 // react-testing-library renders your components to document.body,
 // this adds jest-dom's custom assertions
-import '@testing-library/jest-dom/extend-expect';
+import "@testing-library/jest-dom/extend-expect";
+import "jest-axe/extend-expect";
diff --git a/console/react/src/tableToolbar.test.js b/console/react/src/tableToolbar.test.js
deleted file mode 100644
index 634d26e..0000000
--- a/console/react/src/tableToolbar.test.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from "react";
-import { render, fireEvent } from '@testing-library/react';
-import TableToolbar from "./tableToolbar";
-
-it("should render tableToolbar", () => {
-  let handleChangeFilterValueCalled = false;
-  const props = {
-    fields: [{ title: "field0" }, { title: "field1" }],
-    filterBy: { value: "f" },
-    handleChangeFilterValue: () => handleChangeFilterValueCalled = true,
-    total: 2,
-    page: 1,
-    perPage: 1,
-    onSetPage: () => { },
-    onPerPageSelect: () => { }
-  }
-  const { getByLabelText } = render(
-    <TableToolbar {...props} />
-  );
-  expect(getByLabelText("toolbar-pagination")).toBeInTheDocument()
-  const filterInput = getByLabelText("search text input");
-  expect(filterInput).toBeInTheDocument()
-
-  fireEvent.change(filterInput, { target: { value: 'fi' } });
-  expect(handleChangeFilterValueCalled).toBe(true);
-
-  const paginationInput = getByLabelText("Current page");
-  expect(paginationInput).toBeInTheDocument();
-});
diff --git a/console/react/src/topology/clientInfoComponent.js b/console/react/src/topology/clientInfoComponent.js
index b05462b..feb6e5f 100644
--- a/console/react/src/topology/clientInfoComponent.js
+++ b/console/react/src/topology/clientInfoComponent.js
@@ -29,7 +29,7 @@ import {
 import { CodeBranchIcon } from "@patternfly/react-icons";
 
 import DetailsTable from "./clientInfoDetailsComponent";
-import { utils } from "../amqp/utilities.js";
+import { utils } from "../common/amqp/utilities.js";
 const { queue } = require("d3-queue");
 
 class ClientInfoComponent extends Component {
diff --git a/console/react/src/updated.js b/console/react/src/topology/clientInfoDetailsComponent.js
similarity index 62%
rename from console/react/src/updated.js
rename to console/react/src/topology/clientInfoDetailsComponent.js
index e762d8e..5045309 100644
--- a/console/react/src/updated.js
+++ b/console/react/src/topology/clientInfoDetailsComponent.js
@@ -17,23 +17,27 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import React, { Component } from "react";
+import React from "react";
+import { Table, TableHeader, TableBody } from "@patternfly/react-table";
 
-class Updated extends Component {
+class DetailsTable extends React.Component {
   constructor(props) {
     super(props);
-    this.state = {};
+    this.state = {
+      columns: ["Link type", "Addr", "Settle rate", "Delayed1", "Delayed10", "Usage"]
+    };
   }
 
   render() {
+    const { columns } = this.state;
+    let subRows = this.props.subRows || [];
     return (
-      <pre aria-label="last-updated" data-pf-content="true" className="overview-loading">
-        {`Updated ${this.props.service.utilities.strDate(
-          this.props.lastUpdated
-        )}`}
-      </pre>
+      <Table aria-label="client-info-details-table" cells={columns} rows={subRows}>
+        <TableHeader />
+        <TableBody />
+      </Table>
     );
   }
 }
 
-export default Updated;
+export default DetailsTable;
diff --git a/console/react/src/topology/clientInfoDetailsComponent.jsx b/console/react/src/topology/clientInfoDetailsComponent.jsx
deleted file mode 100644
index 478467b..0000000
--- a/console/react/src/topology/clientInfoDetailsComponent.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from "react";
-import { Table, TableHeader, TableBody } from "@patternfly/react-table";
-
-class DetailsTable extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {
-      columns: ["Link type", "Addr", "Settle rate", "Delayed1", "Delayed10", "Usage"]
-    };
-  }
-
-  render() {
-    const { columns } = this.state;
-    let subRows = this.props.subRows || [];
-    return (
-      <Table aria-label="client-info-details-table" cells={columns} rows={subRows}>
-        <TableHeader />
-        <TableBody />
-      </Table>
-    );
-  }
-}
-
-export default DetailsTable;
diff --git a/console/react/src/topology/contextMenu.js b/console/react/src/topology/contextMenu.js
index f68751d..abfb77a 100644
--- a/console/react/src/topology/contextMenu.js
+++ b/console/react/src/topology/contextMenu.js
@@ -18,7 +18,7 @@ under the License.
 */
 
 import React, { Component } from "react";
-import ContextMenuComponent from "../contextMenuComponent";
+import ContextMenuComponent from "../common/contextMenuComponent";
 
 class ContextMenu extends Component {
   constructor(props) {
diff --git a/console/react/src/topology/legend.js b/console/react/src/topology/legend.js
index 82a49d7..76fbf1b 100644
--- a/console/react/src/topology/legend.js
+++ b/console/react/src/topology/legend.js
@@ -1,18 +1,21 @@
 /*
- * Copyright 2018 Red Hat Inc.
- *
- * Licensed 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.
- */
+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.
+*/
 
 import { Nodes } from "./nodes.js";
 import { appendCircle, appendContent, appendTitle } from "./svgUtils.js";
@@ -71,8 +74,7 @@ const lookFor = [
     role: "route-container",
     title: "Qpid broker",
     text: "Qpid broker",
-    cmp: n =>
-      n.nodeType === "route-container" && n.properties.product === "qpid-cpp",
+    cmp: n => n.nodeType === "route-container" && n.properties.product === "qpid-cpp",
     props: { product: "qpid-cpp" }
   },
   {
@@ -80,8 +82,7 @@ const lookFor = [
     title: "Service",
     text: "Service",
     cmp: n =>
-      n.nodeType === "route-container" &&
-      n.properties.product === "External Service",
+      n.nodeType === "route-container" && n.properties.product === "External Service",
     props: { product: " External Service" }
   }
 ];
@@ -99,10 +100,7 @@ export class Legend {
       .attr("id", "svglegend")
       .attr("xmlns", "http://www.w3.org/2000/svg")
       .append("svg:g")
-      .attr(
-        "transform",
-        `translate(${Nodes.maxRadius()}, ${Nodes.maxRadius()})`
-      )
+      .attr("transform", `translate(${Nodes.maxRadius()}, ${Nodes.maxRadius()})`)
       .selectAll("g");
   }
 
diff --git a/console/react/src/topology/links.js b/console/react/src/topology/links.js
index 1e54288..b05663c 100644
--- a/console/react/src/topology/links.js
+++ b/console/react/src/topology/links.js
@@ -17,7 +17,7 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import { utils } from "../amqp/utilities.js";
+import { utils } from "../common/amqp/utilities.js";
 
 class Link {
   constructor(source, target, dir, cls, uid) {
@@ -29,15 +29,9 @@ class Link {
     this.uid = uid;
   }
   markerId(end) {
-    let selhigh = this.highlighted
-      ? "highlighted"
-      : this.selected
-      ? "selected"
-      : "";
-    if (selhigh === "" && (!this.left && !this.right)) selhigh = "unknown";
-    return `-${selhigh}-${
-      end === "end" ? this.target.radius() : this.source.radius()
-    }`;
+    let selhigh = this.highlighted ? "highlighted" : this.selected ? "selected" : "";
+    if (selhigh === "" && !this.left && !this.right) selhigh = "unknown";
+    return `-${selhigh}-${end === "end" ? this.target.radius() : this.source.radius()}`;
   }
 }
 
@@ -92,17 +86,11 @@ export class Links {
   }
 
   getPosition(name, nodes, source, client, height, localStorage) {
-    let position = localStorage[name]
-      ? JSON.parse(localStorage[name])
-      : undefined;
+    let position = localStorage[name] ? JSON.parse(localStorage[name]) : undefined;
     if (typeof position === "undefined") {
       position = {
-        x: Math.round(
-          nodes.get(source).x + 40 * Math.sin(client / (Math.PI * 2.0))
-        ),
-        y: Math.round(
-          nodes.get(source).y + 40 * Math.cos(client / (Math.PI * 2.0))
-        ),
+        x: Math.round(nodes.get(source).x + 40 * Math.sin(client / (Math.PI * 2.0))),
+        y: Math.round(nodes.get(source).y + 40 * Math.cos(client / (Math.PI * 2.0))),
         fixed: false,
         animate: true
       };
@@ -148,9 +136,7 @@ export class Links {
 
         // we need a unique connection.container
         if (connection.container === "") {
-          connection.container = connection.name
-            .replace("/", "")
-            .replace(":", "-");
+          connection.container = connection.name.replace("/", "").replace(":", "-");
           //utils.uuidv4();
         }
         // this is a connection to another interior router
diff --git a/console/react/src/topology/nodes.js b/console/react/src/topology/nodes.js
index dcad31c..1ae1337 100644
--- a/console/react/src/topology/nodes.js
+++ b/console/react/src/topology/nodes.js
@@ -17,7 +17,7 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import { utils } from "../amqp/utilities.js";
+import { utils } from "../common/amqp/utilities.js";
 import * as d3 from "d3";
 
 export class Node {
diff --git a/console/react/src/topology/svgUtils.js b/console/react/src/topology/svgUtils.js
index 873a0c4..c9d9477 100644
--- a/console/react/src/topology/svgUtils.js
+++ b/console/react/src/topology/svgUtils.js
@@ -1,21 +1,24 @@
 /*
- * Copyright 2018 Red Hat Inc.
- *
- * Licensed 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.
- */
+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.
+*/
 
 import { Nodes } from "./nodes.js";
-import { utils } from "../amqp/utilities.js";
+import { utils } from "../common/amqp/utilities.js";
 
 // update the node's classes based on the node's data
 export function updateState(circle) {
diff --git a/console/react/src/topology/topoUtils.js b/console/react/src/topology/topoUtils.js
index 1f04d05..58f1a5b 100644
--- a/console/react/src/topology/topoUtils.js
+++ b/console/react/src/topology/topoUtils.js
@@ -18,7 +18,7 @@ under the License.
 */
 
 /* global Set */
-import { utils } from "../amqp/utilities.js";
+import { utils } from "../common/amqp/utilities.js";
 
 // highlight the paths between the selected node and the hovered node
 function findNextHopNode(from, d, nodeInfo, selected_node, nodes) {
@@ -43,15 +43,7 @@ function findNextHopNode(from, d, nodeInfo, selected_node, nodes) {
   }
   return null;
 }
-export function nextHop(
-  thisNode,
-  d,
-  nodes,
-  links,
-  nodeInfo,
-  selected_node,
-  cb
-) {
+export function nextHop(thisNode, d, nodes, links, nodeInfo, selected_node, cb) {
   if (thisNode && thisNode !== d) {
     let target = findNextHopNode(thisNode, d, nodeInfo, selected_node, nodes);
     if (target) {
@@ -74,7 +66,7 @@ export function connectionPopupHTML(d, nodeInfo) {
     return;
   }
   // return all of onode's connections that connecto to right
-  let getConnsArray = function (onode, key, right) {
+  let getConnsArray = function(onode, key, right) {
     if (right.normals) {
       // if we want connections between a router and a client[s]
       let connIds = new Set();
@@ -84,16 +76,16 @@ export function connectionPopupHTML(d, nodeInfo) {
         if (normal.key === key) {
           connIds.add(normal.connectionId);
         } else if (normal.alsoConnectsTo) {
-          normal.alsoConnectsTo.forEach(function (ac2) {
+          normal.alsoConnectsTo.forEach(function(ac2) {
             if (ac2.key === key) connIds.add(ac2.connectionId);
           });
         }
       }
       return onode.connection.results
-        .filter(function (result) {
+        .filter(function(result) {
           return connIds.has(result[connIndex]);
         })
-        .map(function (c) {
+        .map(function(c) {
           return utils.flatten(onode.connection.attributeNames, c);
         });
     } else {
@@ -102,20 +94,17 @@ export function connectionPopupHTML(d, nodeInfo) {
       let containerIndex = onode.connection.attributeNames.indexOf("container");
       let roleIndex = onode.connection.attributeNames.indexOf("role");
       return onode.connection.results
-        .filter(function (conn) {
-          return (
-            conn[containerIndex] === container &&
-            conn[roleIndex] === "inter-router"
-          );
+        .filter(function(conn) {
+          return conn[containerIndex] === container && conn[roleIndex] === "inter-router";
         })
-        .map(function (c) {
+        .map(function(c) {
           return utils.flatten(onode.connection.attributeNames, c);
         });
     }
   };
   // construct HTML to be used in a popup when the mouse is moved over a link.
   // The HTML is sanitized elsewhere before it is displayed
-  let linksHTML = function (onode, conns) {
+  let linksHTML = function(onode, conns) {
     const max_links = 10;
     const fields = [
       "deliveryCount",
@@ -126,13 +115,13 @@ export function connectionPopupHTML(d, nodeInfo) {
       "modifiedCount"
     ];
     // local function to determine if a link's connectionId is in any of the connections
-    let isLinkFor = function (connectionId, conns) {
+    let isLinkFor = function(connectionId, conns) {
       for (let c = 0; c < conns.length; c++) {
         if (conns[c].identity === connectionId) return true;
       }
       return false;
     };
-    let fnJoin = function (ar, sepfn) {
+    let fnJoin = function(ar, sepfn) {
       let out = "";
       out = ar[0];
       for (let i = 1; i < ar.length; i++) {
@@ -154,13 +143,7 @@ export function connectionPopupHTML(d, nodeInfo) {
         if (isLinkFor(link.connectionId, conns)) {
           if (link.owningAddr) hasAddress = true;
           if (link.name) {
-            let rates = utils.rates(
-              link,
-              fields,
-              linkRateHistory,
-              link.name,
-              1
-            );
+            let rates = utils.rates(link, fields, linkRateHistory, link.name, 1);
             // replace the raw value with the rate
             for (let i = 0; i < fields.length; i++) {
               if (rates[fields[i]] > 0) allZero = false;
@@ -172,14 +155,14 @@ export function connectionPopupHTML(d, nodeInfo) {
       }
     }
     // we may need to limit the number of links displayed, so sort descending by the sum of the field values
-    let sum = function (a) {
+    let sum = function(a) {
       let s = 0;
       for (let i = 0; i < fields.length; i++) {
         s += a[fields[i]];
       }
       return s;
     };
-    links.sort(function (a, b) {
+    links.sort(function(a, b) {
       let asum = sum(a);
       let bsum = sum(b);
       return asum < bsum ? 1 : asum > bsum ? -1 : 0;
@@ -200,15 +183,14 @@ export function connectionPopupHTML(d, nodeInfo) {
       td.unshift("owningAddr");
     }
 
-    let rate_th = function (th) {
-      let rth = th.map(function (t) {
+    let rate_th = function(th) {
+      let rth = th.map(function(t) {
         if (t.endsWith("Count")) t = t.replace("Count", "Rate");
         return utils.humanify(t);
       });
       return rth;
     };
-    HTML +=
-      '<tr class="header"><td>' + rate_th(th).join("</td><td>") + "</td></tr>";
+    HTML += '<tr class="header"><td>' + rate_th(th).join("</td><td>") + "</td></tr>";
     // add rows to the table for each link
     for (let l = 0; l < links.length; l++) {
       if (l >= max_links) {
@@ -216,14 +198,14 @@ export function connectionPopupHTML(d, nodeInfo) {
         break;
       }
       let link = links[l];
-      let vals = td.map(function (f) {
+      let vals = td.map(function(f) {
         if (f === "owningAddr") {
           let identity = utils.identity_clean(link.owningAddr);
           return utils.addr_text(identity);
         }
         return link[f];
       });
-      let joinedVals = fnJoin(vals, function (v1, last) {
+      let joinedVals = fnJoin(vals, function(v1, last) {
         return [
           `</td><td${isNaN(+v1) ? "" : ' align="right"'}>`,
           last ? v1 : utils.pretty(v1 || "0", ",.2f")
@@ -270,7 +252,8 @@ export function connectionPopupHTML(d, nodeInfo) {
 
 export function getSizes(topologyRef) {
   const gap = 5;
-  let topoWidth = topologyRef.offsetWidth > 0 ? topologyRef.offsetWidth : window.innerWidth;
+  let topoWidth =
+    topologyRef.offsetWidth > 0 ? topologyRef.offsetWidth : window.innerWidth;
   let width = topoWidth - gap;
   let top = topologyRef.offsetTop;
   let height = window.innerHeight - top - gap;
diff --git a/console/react/src/topology/topologyPage.test.js b/console/react/src/topology/topologyPage.test.js
index 2b99aed..f8d4ed2 100644
--- a/console/react/src/topology/topologyPage.test.js
+++ b/console/react/src/topology/topologyPage.test.js
@@ -1,6 +1,25 @@
+/*
+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.
+*/
+
 import React from "react";
 import { render } from "@testing-library/react";
-import { mockService } from "../qdrService.mock";
+import { mockService } from "../../test_data/qdrService.mock";
 import TopologyPage from "./topologyPage";
 
 it("renders the TopologyPage component", () => {
diff --git a/console/react/src/topology/topologyToolbar.js b/console/react/src/topology/topologyToolbar.js
index d04d90c..071807f 100644
--- a/console/react/src/topology/topologyToolbar.js
+++ b/console/react/src/topology/topologyToolbar.js
@@ -1,15 +1,36 @@
+/*
+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.
+*/
+
 import React from "react";
 import { Toolbar, ToolbarGroup, ToolbarItem } from "@patternfly/react-core";
 import TrafficComponent from "./trafficComponent";
 import MapComponent from "./mapComponent";
 import ArrowsComponent from "./arrowsComponent";
-import DropdownPanel from "../dropdownPanel"
-
+import DropdownPanel from "../common/dropdownPanel";
 
 class TopologyToolbar extends React.Component {
   render() {
     return (
-      <Toolbar data-testid="topology-toolbar" className="pf-l-toolbar pf-u-justify-content-space-between pf-u-mx-xl pf-u-my-md">
+      <Toolbar
+        data-testid="topology-toolbar"
+        className="pf-l-toolbar pf-u-justify-content-space-between pf-u-mx-xl pf-u-my-md"
+      >
         <ToolbarGroup>
           <ToolbarItem className="pf-u-mr-md">
             <DropdownPanel
@@ -18,9 +39,7 @@ class TopologyToolbar extends React.Component {
                 <TrafficComponent
                   addresses={this.props.legendOptions.traffic.addresses}
                   addressColors={this.props.legendOptions.traffic.addressColors}
-                  handleChangeTrafficAnimation={
-                    this.props.handleChangeTrafficAnimation
-                  }
+                  handleChangeTrafficAnimation={this.props.handleChangeTrafficAnimation}
                   handleChangeTrafficFlowAddress={
                     this.props.handleChangeTrafficFlowAddress
                   }
diff --git a/console/react/src/topology/topologyToolbar.test.js b/console/react/src/topology/topologyToolbar.test.js
index 53be27e..317602b 100644
--- a/console/react/src/topology/topologyToolbar.test.js
+++ b/console/react/src/topology/topologyToolbar.test.js
@@ -1,8 +1,27 @@
+/*
+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.
+*/
+
 import React from "react";
-import { render } from '@testing-library/react';
+import { render } from "@testing-library/react";
 import TopologyToolbar from "./topologyToolbar";
 
-it('renders a TopologyToolbar', () => {
+it("renders a TopologyToolbar", () => {
   const props = {
     legendOptions: {
       traffic: {
@@ -19,18 +38,15 @@ it('renders a TopologyToolbar', () => {
       arrows: { routerArrows: true, clientArrows: true }
     },
     mapOptions: { areaColor: "#EAEAEA", oceanColor: "#EBAEBA" },
-    handleChangeTrafficAnimation: () => { },
-    handleChangeTrafficFlowAddress: () => { },
-    handleHoverAddress: () => { },
-    handleUpdateMapColor: () => { },
-    handleUpdateMapShown: () => { },
-    handleChangeArrows: () => { }
-  }
-  const {
-    getByTestId
-  } = render(<TopologyToolbar {...props} />);
+    handleChangeTrafficAnimation: () => {},
+    handleChangeTrafficFlowAddress: () => {},
+    handleHoverAddress: () => {},
+    handleUpdateMapColor: () => {},
+    handleUpdateMapShown: () => {},
+    handleChangeArrows: () => {}
+  };
+  const { getByTestId } = render(<TopologyToolbar {...props} />);
 
   // the toolbar should be present
   expect(getByTestId("topology-toolbar")).toBeInTheDocument();
-
-})
+});
diff --git a/console/react/src/topology/topologyViewer.js b/console/react/src/topology/topologyViewer.js
index 9d7a0af..5871970 100644
--- a/console/react/src/topology/topologyViewer.js
+++ b/console/react/src/topology/topologyViewer.js
@@ -26,14 +26,14 @@ import {
   TopologySideBar
 } from "@patternfly/react-topology";
 
-import QDRPopup from "../qdrPopup";
+import QDRPopup from "../common/qdrPopup";
 import { Traffic } from "./traffic.js";
 import { separateAddresses } from "../chord/filters.js";
 import { Nodes } from "./nodes.js";
 import { Links } from "./links.js";
 import { nextHop, connectionPopupHTML, getSizes } from "./topoUtils.js";
 import { BackgroundMap } from "./map.js";
-import { utils } from "../amqp/utilities.js";
+import { utils } from "../common/amqp/utilities.js";
 import { Legend } from "./legend.js";
 import RouterInfoComponent from "./routerInfoComponent";
 import ClientInfoComponent from "./clientInfoComponent";
@@ -47,7 +47,7 @@ import {
   addDefs,
   updateState
 } from "./svgUtils.js";
-import { QDRLogger } from "../qdrGlobals";
+import { QDRLogger } from "../common/qdrGlobals";
 const TOPOOPTIONSKEY = "topologyLegendOptions";
 
 class TopologyPage extends Component {
diff --git a/console/react/src/topology/topologyViewer.test.js b/console/react/src/topology/topologyViewer.test.js
index 382b950..a2a093e 100644
--- a/console/react/src/topology/topologyViewer.test.js
+++ b/console/react/src/topology/topologyViewer.test.js
@@ -1,6 +1,25 @@
+/*
+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.
+*/
+
 import React from "react";
 import { render, fireEvent, waitForElement } from "@testing-library/react";
-import { mockService } from "../qdrService.mock";
+import { service, login } from "../serviceTest";
 import TopologyViewer from "./topologyViewer";
 import * as world from "../../public/data/countries.json";
 
@@ -11,8 +30,11 @@ it("renders the TopologyViewer component", async () => {
     });
   });
   const props = {
-    service: mockService({})
+    service
   };
+
+  await login();
+
   const { getByLabelText, getByText, getByTestId } = render(
     <TopologyViewer {...props} />
   );
@@ -49,12 +71,24 @@ it("renders the TopologyViewer component", async () => {
   // close the modal
   fireEvent.click(getByLabelText("Close"));
 
+  // make sure the legend opens
   const legendButton = getByText("topology-legend");
   expect(legendButton).toBeInTheDocument();
-
   fireEvent.click(legendButton);
   fireEvent.click(getByLabelText("Close"));
 
+  // turn on the traffic animation
+
+  // dropdown the traffic panel
   fireEvent.contextMenu(client);
   fireEvent.click(pfTopologyView);
+  const trafficButton = getByLabelText("button-for-Traffic");
+  expect(trafficButton).toBeInTheDocument();
+  fireEvent.click(trafficButton);
+
+  // click on the show traffic checkbox
+  const trafficCheckbox = getByLabelText("show traffic by address");
+  fireEvent.click(trafficCheckbox);
+  // the address dot should be there
+  await waitForElement(() => getByText("toB"));
 });
diff --git a/console/react/src/topology/traffic.js b/console/react/src/topology/traffic.js
index ed49d96..b15b9fa 100644
--- a/console/react/src/topology/traffic.js
+++ b/console/react/src/topology/traffic.js
@@ -21,7 +21,7 @@ import * as d3 from "d3";
 import { ChordData } from "../chord/data.js";
 import { MIN_CHORD_THRESHOLD } from "../chord/matrix.js";
 import { nextHop } from "./topoUtils.js";
-import { utils } from "../amqp/utilities.js";
+import { utils } from "../common/amqp/utilities.js";
 
 const transitionDuration = 1000;
 //const CHORDFILTERKEY = "chordFilter";
@@ -76,9 +76,7 @@ export class Traffic {
   addAnimationType(type, converter, radius) {
     if (!this.viss.some(v => v.type === type)) {
       this.viss.push(
-        type === "dots"
-          ? new Dots(this, converter, radius)
-          : new Congestion(this)
+        type === "dots" ? new Dots(this, converter, radius) : new Congestion(this)
       );
     }
     this.start();
@@ -125,10 +123,7 @@ class Congestion extends TrafficAnimation {
     if (attrIndex >= 0) {
       for (let i = 0; i < node[entity].results.length; i++) {
         if (node[entity].results[i][attrIndex] === value) {
-          return utils.flatten(
-            node[entity].attributeNames,
-            node[entity].results[i]
-          );
+          return utils.flatten(node[entity].attributeNames, node[entity].results[i]);
         }
       }
     }
@@ -166,10 +161,7 @@ class Congestion extends TrafficAnimation {
               nodeLinks.results[n]
             );
             if (link.linkType !== "router-control") {
-              let f = self.nodeIndexFor(
-                nodes,
-                srv.utilities.nameFromId(nodeId)
-              );
+              let f = self.nodeIndexFor(nodes, srv.utilities.nameFromId(nodeId));
               let connection = self.findResult(
                 node,
                 "connection",
@@ -181,11 +173,9 @@ class Congestion extends TrafficAnimation {
                 let little = Math.min(f, t);
                 let big = Math.max(f, t);
                 if (little >= 0) {
-                  let key = [
-                    "#path",
-                    nodes[little].uid(srv),
-                    nodes[big].uid(srv)
-                  ].join("-");
+                  let key = ["#path", nodes[little].uid(srv), nodes[big].uid(srv)].join(
+                    "-"
+                  );
                   if (!links[key]) links[key] = [];
                   links[key].push(link);
                 }
@@ -295,8 +285,7 @@ class Dots extends TrafficAnimation {
   updateAddresses() {
     this.excludedAddresses = [];
     for (const address in this.traffic.addresses) {
-      if (!this.traffic.addresses[address])
-        this.excludedAddresses.push(address);
+      if (!this.traffic.addresses[address]) this.excludedAddresses.push(address);
     }
     if (this.chordData) {
       this.chordData.setFilter(this.excludedAddresses);
@@ -423,8 +412,10 @@ class Dots extends TrafficAnimation {
             this.addressIndex(this, ahop.address) +
             (ahop.back ? "b" : "");
           let path = d3.select("#path" + pathId);
-          // start the animation. If the animation is already running, this will have no effect
-          this.startAnimation(path, flowId, ahop, flowScale(ahop.val));
+          if (!path.empty()) {
+            // start the animation. If the animation is already running, this will have no effect
+            this.startAnimation(path, flowId, ahop, flowScale(ahop.val));
+          }
           keep[flowId] = true;
         }
       }
@@ -446,10 +437,7 @@ class Dots extends TrafficAnimation {
       .transition()
       .ease("easeLinear")
       .duration((l * 10) / rate)
-      .attrTween(
-        "transform",
-        this.translateDots(this.radius, path, count, back)
-      )
+      .attrTween("transform", this.translateDots(this.radius, path, count, back))
       .each("end", () => {
         if (this.stopped === false) {
           this.animateFlow(flow, path, count, back, rate);
@@ -465,6 +453,7 @@ class Dots extends TrafficAnimation {
     let back = hop.back,
       address = hop.address;
     // the density of dots is determined by the rate of this traffic relative to the other traffic
+    if (!path.node().getTotalLength) return;
     let len = Math.max(Math.floor(path.node().getTotalLength() / 50), 1);
     let dots = [];
     for (let i = 0, offset = this.addressIndex(this, address); i < len; ++i) {
@@ -494,6 +483,7 @@ class Dots extends TrafficAnimation {
       .enter()
       .append("circle")
       .attr("class", "flow flow" + id)
+      .attr("data-testid", (d, i) => `flow${id}-${i}`)
       .attr("fill", this.fillColor(address, this.traffic.addressColors))
       .attr("r", 4);
     this.animateFlow(circles, path, dots.length, back, rate);
diff --git a/console/react/src/topology/trafficComponent.js b/console/react/src/topology/trafficComponent.js
index 46c6023..ab377fb 100644
--- a/console/react/src/topology/trafficComponent.js
+++ b/console/react/src/topology/trafficComponent.js
@@ -19,7 +19,7 @@ under the License.
 
 import React, { Component } from "react";
 import { Checkbox } from "@patternfly/react-core";
-import AddressesComponent from "../addressesComponent";
+import AddressesComponent from "../common/addressesComponent";
 
 class TrafficComponent extends Component {
   constructor(props) {
@@ -47,9 +47,7 @@ class TrafficComponent extends Component {
                 <AddressesComponent
                   addresses={this.props.addresses}
                   addressColors={this.props.addressColors}
-                  handleChangeAddress={
-                    this.props.handleChangeTrafficFlowAddress
-                  }
+                  handleChangeAddress={this.props.handleChangeTrafficFlowAddress}
                   handleHoverAddress={this.props.handleHoverAddress}
                 />
               </li>
@@ -89,10 +87,7 @@ class TrafficComponent extends Component {
                     x2="100%"
                     y2="0%"
                   >
-                    <stop
-                      style={{ stopColor: "#999999", stopOpacity: 1 }}
-                      offset="0"
-                    />
+                    <stop style={{ stopColor: "#999999", stopOpacity: 1 }} offset="0" />
                     <stop
                       style={{ stopColor: "#00FF00", stopOpacity: 1 }}
                       offset="0.333"
@@ -101,10 +96,7 @@ class TrafficComponent extends Component {
                       style={{ stopColor: "#FFA500", stopOpacity: 1 }}
                       offset="0.666"
                     />
-                    <stop
-                      style={{ stopColor: "#FF0000", stopOpacity: 1 }}
-                      offset="1"
-                    />
+                    <stop style={{ stopColor: "#FF0000", stopOpacity: 1 }} offset="1" />
                   </linearGradient>
                 </defs>
                 <g>
diff --git a/console/react/src/updated.test.js b/console/react/src/updated.test.js
deleted file mode 100644
index 28353ae..0000000
--- a/console/react/src/updated.test.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from "react";
-import { render } from '@testing-library/react';
-import { QDRService } from "./qdrService";
-import Updated from "./updated";
-
-it("should render the Updated component", () => {
-  const service = new QDRService(() => { });
-  const props = {
-    lastUpdated: new Date(),
-    service
-  }
-  const { getByLabelText } = render(
-    <Updated {...props} />
-  );
-  expect(getByLabelText("last-updated")).toBeInTheDocument()
-});
diff --git a/console/react/src/qdrService.mock.js b/console/react/test_data/qdrService.mock.js
similarity index 88%
rename from console/react/src/qdrService.mock.js
rename to console/react/test_data/qdrService.mock.js
index c1229f9..b247a63 100644
--- a/console/react/src/qdrService.mock.js
+++ b/console/react/test_data/qdrService.mock.js
@@ -17,10 +17,10 @@ specific language governing permissions and limitations
 under the License.
 */
 
-import { utils } from "./amqp/utilities";
-import fetchResults from "../test_data/fetchEntities";
-import nodeInfo from "../test_data/nodeInfo";
-import schema from "../test_data/schema";
+import { utils } from "../src/common/amqp/utilities";
+import fetchResults from "./fetchEntities";
+import nodeInfo from "./nodeInfo";
+import schema from "./schema";
 
 const methodResults = {
   context: {
@@ -41,7 +41,9 @@ export const mockService = ({ onSendMethod }) => {
         sendMethod: () => {
           cbSendMethod();
           return Promise.resolve(methodResults);
-        }
+        },
+        is_connected: () => true,
+        setReconnect: () => {}
       },
       topology: {
         setUpdateEntities: () => {},
diff --git a/console/react/yarn.lock b/console/react/yarn.lock
index 7c28ef6..6230861 100644
--- a/console/react/yarn.lock
+++ b/console/react/yarn.lock
@@ -9,26 +9,6 @@
   dependencies:
     "@babel/highlight" "^7.0.0"
 
-"@babel/core@7.4.3":
-  version "7.4.3"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.3.tgz#198d6d3af4567be3989550d97e068de94503074f"
-  integrity sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==
-  dependencies:
-    "@babel/code-frame" "^7.0.0"
-    "@babel/generator" "^7.4.0"
-    "@babel/helpers" "^7.4.3"
-    "@babel/parser" "^7.4.3"
-    "@babel/template" "^7.4.0"
-    "@babel/traverse" "^7.4.3"
-    "@babel/types" "^7.4.0"
-    convert-source-map "^1.1.0"
-    debug "^4.1.0"
-    json5 "^2.1.0"
-    lodash "^4.17.11"
-    resolve "^1.3.2"
-    semver "^5.4.1"
-    source-map "^0.5.0"
-
 "@babel/core@7.6.0":
   version "7.6.0"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.0.tgz#9b00f73554edd67bebc86df8303ef678be3d7b48"
@@ -49,7 +29,7 @@
     semver "^5.4.1"
     source-map "^0.5.0"
 
-"@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.4.5":
+"@babel/core@^7.1.0", "@babel/core@^7.4.5":
   version "7.7.2"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.2.tgz#ea5b99693bcfc058116f42fa1dd54da412b29d91"
   integrity sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==
@@ -262,7 +242,7 @@
     "@babel/traverse" "^7.7.0"
     "@babel/types" "^7.7.0"
 
-"@babel/helpers@^7.4.3", "@babel/helpers@^7.6.0", "@babel/helpers@^7.7.0":
+"@babel/helpers@^7.6.0", "@babel/helpers@^7.7.0":
   version "7.7.0"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.0.tgz#359bb5ac3b4726f7c1fde0ec75f64b3f4275d60b"
   integrity sha512-VnNwL4YOhbejHb7x/b5F39Zdg5vIQpUUNzJwx0ww1EcVRt41bbGRZWhAURrfY32T5zTT3qwNOQFWpn+P0i0a2g==
@@ -796,7 +776,7 @@
     js-levenshtein "^1.1.3"
     semver "^5.5.0"
 
-"@babel/preset-env@^7.1.6":
+"@babel/preset-env@^7.4.5":
   version "7.7.1"
   resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.7.1.tgz#04a2ff53552c5885cf1083e291c8dd5490f744bb"
   integrity sha512-/93SWhi3PxcVTDpSqC+Dp4YxUu3qZ4m7I76k0w73wYfn7bGVuRIO4QUz95aJksbS+AD1/mT1Ie7rbkT0wSplaA==
@@ -883,14 +863,6 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/plugin-transform-typescript" "^7.6.0"
 
-"@babel/runtime-corejs2@^7.0.0":
-  version "7.7.2"
-  resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.7.2.tgz#5a8c4e2f8688ce58adc9eb1d8320b6e7341f96ce"
-  integrity sha512-GfVnHchOBvIMsweQ13l4jd9lT4brkevnavnVOej5g2y7PpTRY+R4pcQlCjWMZoUla5rMLFzaS/Ll2s59cB1TqQ==
-  dependencies:
-    core-js "^2.6.5"
-    regenerator-runtime "^0.13.2"
-
 "@babel/runtime@7.6.0":
   version "7.6.0"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
@@ -898,7 +870,7 @@
   dependencies:
     regenerator-runtime "^0.13.2"
 
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2":
   version "7.7.2"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a"
   integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==
@@ -946,35 +918,6 @@
     exec-sh "^0.3.2"
     minimist "^1.2.0"
 
-"@commitlint/execute-rule@^8.2.0":
-  version "8.2.0"
-  resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-8.2.0.tgz#aefb3744e22613660adefb7ebcccaa60bd24e78d"
-  integrity sha512-9MBRthHaulbWTa8ReG2Oii2qc117NuvzhZdnkuKuYLhker7sUXGFcVhLanuWUKGyfyI2o9zVr/NHsNbCCsTzAA==
-
-"@commitlint/load@>6.1.1":
-  version "8.2.0"
-  resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-8.2.0.tgz#9ca53a0c795e4f63d796b4d42279e856549add1a"
-  integrity sha512-EV6PfAY/p83QynNd1llHxJiNxKmp43g8+7dZbyfHFbsGOdokrCnoelAVZ+WGgktXwLN/uXyfkcIAxwac015UYw==
-  dependencies:
-    "@commitlint/execute-rule" "^8.2.0"
-    "@commitlint/resolve-extends" "^8.2.0"
-    babel-runtime "^6.23.0"
-    chalk "2.4.2"
-    cosmiconfig "^5.2.0"
-    lodash "4.17.14"
-    resolve-from "^5.0.0"
-
-"@commitlint/resolve-extends@^8.2.0":
-  version "8.2.0"
-  resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-8.2.0.tgz#b7f2f0c71c10f24b98a199ed11d2c14cfd7a318f"
-  integrity sha512-cwi0HUsDcD502HBP8huXfTkVuWmeo1Fiz3GKxNwMBBsJV4+bKa7QrtxbNpXhVuarX7QjWfNTvmW6KmFS7YK9uw==
-  dependencies:
-    "@types/node" "^12.0.2"
-    import-fresh "^3.0.0"
-    lodash "4.17.14"
-    resolve-from "^5.0.0"
-    resolve-global "^1.0.0"
-
 "@csstools/convert-colors@^1.4.0":
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
@@ -1045,9 +988,9 @@
     "@fortawesome/fontawesome-common-types" "^0.2.25"
 
 "@hapi/address@2.x.x":
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.2.tgz#1c794cd6dbf2354d1eb1ef10e0303f573e1c7222"
-  integrity sha512-O4QDrx+JoGKZc6aN64L04vqa7e41tIiLU+OvKdcYaEMP97UttL0f9GIi9/0A4WAMx0uBd6SidDIhktZhgOcN8Q==
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
+  integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==
 
 "@hapi/bourne@1.x.x":
   version "1.3.2"
@@ -1215,7 +1158,7 @@
     source-map "^0.6.1"
     write-file-atomic "2.4.1"
 
-"@jest/types@^24.7.0", "@jest/types@^24.9.0":
+"@jest/types@^24.9.0":
   version "24.9.0"
   resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59"
   integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==
@@ -1232,89 +1175,11 @@
     call-me-maybe "^1.0.1"
     glob-to-regexp "^0.3.0"
 
-"@nodelib/fs.scandir@2.1.3":
-  version "2.1.3"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
-  integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
-  dependencies:
-    "@nodelib/fs.stat" "2.0.3"
-    run-parallel "^1.1.9"
-
-"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
-  integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
-
 "@nodelib/fs.stat@^1.1.2":
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
   integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
 
-"@nodelib/fs.walk@^1.2.3":
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
-  integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
-  dependencies:
-    "@nodelib/fs.scandir" "2.1.3"
-    fastq "^1.6.0"
-
-"@octokit/endpoint@^5.5.0":
-  version "5.5.1"
-  resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.5.1.tgz#2eea81e110ca754ff2de11c79154ccab4ae16b3f"
-  integrity sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==
-  dependencies:
-    "@octokit/types" "^2.0.0"
-    is-plain-object "^3.0.0"
-    universal-user-agent "^4.0.0"
-
-"@octokit/request-error@^1.0.1", "@octokit/request-error@^1.0.2":
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.0.tgz#a64d2a9d7a13555570cd79722de4a4d76371baaa"
-  integrity sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==
-  dependencies:
-    "@octokit/types" "^2.0.0"
-    deprecation "^2.0.0"
-    once "^1.4.0"
-
-"@octokit/request@^5.2.0":
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.3.1.tgz#3a1ace45e6f88b1be4749c5da963b3a3b4a2f120"
-  integrity sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==
-  dependencies:
-    "@octokit/endpoint" "^5.5.0"
-    "@octokit/request-error" "^1.0.1"
-    "@octokit/types" "^2.0.0"
-    deprecation "^2.0.0"
-    is-plain-object "^3.0.0"
-    node-fetch "^2.3.0"
-    once "^1.4.0"
-    universal-user-agent "^4.0.0"
-
-"@octokit/rest@^16.27.0":
-  version "16.35.0"
-  resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.35.0.tgz#7ccc1f802f407d5b8eb21768c6deca44e7b4c0d8"
-  integrity sha512-9ShFqYWo0CLoGYhA1FdtdykJuMzS/9H6vSbbQWDX4pWr4p9v+15MsH/wpd/3fIU+tSxylaNO48+PIHqOkBRx3w==
-  dependencies:
-    "@octokit/request" "^5.2.0"
-    "@octokit/request-error" "^1.0.2"
-    atob-lite "^2.0.0"
-    before-after-hook "^2.0.0"
-    btoa-lite "^1.0.0"
-    deprecation "^2.0.0"
-    lodash.get "^4.4.2"
-    lodash.set "^4.3.2"
-    lodash.uniq "^4.5.0"
-    octokit-pagination-methods "^1.1.0"
-    once "^1.4.0"
-    universal-user-agent "^4.0.0"
-
-"@octokit/types@^2.0.0":
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.0.1.tgz#0caf0364e010296265621593ac9a37f40ef75dad"
-  integrity sha512-YDYgV6nCzdGdOm7wy43Ce8SQ3M5DMKegB8E5sTB/1xrxOdo2yS/KgUgML2N2ZGD621mkbdrAglwTyA4NDOlFFA==
-  dependencies:
-    "@types/node" ">= 8"
-
 "@patternfly/patternfly@2.40.13":
   version "2.40.13"
   resolved "https://registry.yarnpkg.com/@patternfly/patternfly/-/patternfly-2.40.13.tgz#d1aa7de123dd63972951a67ecc0a6e04117f85db"
@@ -1325,20 +1190,19 @@
   resolved "https://registry.yarnpkg.com/@patternfly/patternfly/-/patternfly-2.41.0.tgz#64437b2b54c91cbfe5a37f2490b6a3fba515eba2"
   integrity sha512-w85M4hr/fhnedIcSyjxeswgdkKREd7Y2To8tl0/EiVlNfmNowZ06NuRXrzmPKjVEB8AWtSwvgCwUo+YWL2ZUJg==
 
-"@patternfly/react-charts@^4.1.5":
-  version "4.9.14"
-  resolved "https://registry.yarnpkg.com/@patternfly/react-charts/-/react-charts-4.9.14.tgz#e2ecf07acfe18053fa868de309d96211e421e493"
-  integrity sha512-f6E9gC0MSXrKiYYC/bksQJDv8n1zwtIAGp/VqggiwRpGrOl4+Bk2Ryok/4b93hEteoyfqBl6oGyKaI5As3tN6g==
+"@patternfly/react-charts@^5.1.6":
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/@patternfly/react-charts/-/react-charts-5.1.6.tgz#848004238d75dde9cb720c0a5d720a3b0f6add00"
+  integrity sha512-T7dPN/URaUgA0+RAzy1TaDK6Xqk4aBT0zoWCPbLGztv3YIIotA/qk68kHFOdHjZ/NMHkZ/rleEzHejQFQgdvcg==
   dependencies:
-    "@patternfly/react-styles" "^3.5.23"
-  optionalDependencies:
-    "@types/lodash" "^4.14.132"
-    "@types/victory" "^31.0.18"
+    "@patternfly/patternfly" "2.40.13"
+    "@patternfly/react-styles" "^3.6.6"
+    "@patternfly/react-tokens" "^2.7.6"
     hoist-non-react-statics "^3.3.0"
-    lodash "^4.17.11"
-    victory "^32.2.3"
-    victory-core "^32.2.3"
-    victory-legend "^32.2.3"
+    lodash "^4.17.15"
+    victory "^33.0.5"
+    victory-core "^33.0.1"
+    victory-legend "^33.0.1"
 
 "@patternfly/react-core@^3.120.8", "@patternfly/react-core@^3.38.1":
   version "3.120.8"
@@ -1360,7 +1224,7 @@
   dependencies:
     "@fortawesome/free-brands-svg-icons" "^5.8.1"
 
-"@patternfly/react-styles@^3.3.3", "@patternfly/react-styles@^3.5.23", "@patternfly/react-styles@^3.6.6":
+"@patternfly/react-styles@^3.3.3", "@patternfly/react-styles@^3.6.6":
   version "3.6.6"
   resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-3.6.6.tgz#d4b481fbd34adc28b3129842aea4834c384d4382"
   integrity sha512-BOn6s9kuwsWRVHeTvA9D55JMYMwoNaQn9eB+MBzX2zIbZxy0TGOTgNifU99lJct+6TFxSUxx81gzpby+xBcDSQ==
@@ -1416,79 +1280,6 @@
     "@babel/runtime" "^7.1.2"
     lodash "^4.17.11"
 
-"@semantic-release/commit-analyzer@^6.1.0":
-  version "6.3.3"
-  resolved "https://registry.yarnpkg.com/@semantic-release/commit-analyzer/-/commit-analyzer-6.3.3.tgz#885f7e46e2f0aef23a23be0904dbf18d6ece45ca"
-  integrity sha512-Pyv1ZL2u5AIOY4YbxFCAB5J1PEh5yON8ylbfiPiriDGGW6Uu1U3Y8lysMtWu+FUD5x7tSnyIzhqx0+fxPxqbgw==
-  dependencies:
-    conventional-changelog-angular "^5.0.0"
-    conventional-commits-filter "^2.0.0"
-    conventional-commits-parser "^3.0.7"
-    debug "^4.0.0"
-    import-from "^3.0.0"
-    lodash "^4.17.4"
-
-"@semantic-release/error@^2.2.0":
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-2.2.0.tgz#ee9d5a09c9969eade1ec864776aeda5c5cddbbf0"
-  integrity sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==
-
-"@semantic-release/github@^5.1.0":
-  version "5.5.5"
-  resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-5.5.5.tgz#4666367f16d8ad91fd1d3c71a7238498de14ec38"
-  integrity sha512-Wo9OIULMRydbq+HpFh9yiLvra1XyEULPro9Tp4T5MQJ0WZyAQ3YQm74IdT8Pe/UmVDq2nfpT1oHrWkwOc4loHg==
-  dependencies:
-    "@octokit/rest" "^16.27.0"
-    "@semantic-release/error" "^2.2.0"
-    aggregate-error "^3.0.0"
-    bottleneck "^2.18.1"
-    debug "^4.0.0"
-    dir-glob "^3.0.0"
-    fs-extra "^8.0.0"
-    globby "^10.0.0"
-    http-proxy-agent "^2.1.0"
-    https-proxy-agent "^3.0.0"
-    issue-parser "^5.0.0"
-    lodash "^4.17.4"
-    mime "^2.4.3"
-    p-filter "^2.0.0"
-    p-retry "^4.0.0"
-    url-join "^4.0.0"
-
-"@semantic-release/npm@^5.0.5":
-  version "5.3.4"
-  resolved "https://registry.yarnpkg.com/@semantic-release/npm/-/npm-5.3.4.tgz#2998cd9455aaedf278334d4a5b56f8e0b715919d"
-  integrity sha512-XjITNRA/oOpJ7BfHk/WaOHs1WniYBszTde/bwADjjk1Luacpxg87jbDQVVt/oA3Zlx+MelxACRIEuRiPC5gu8g==
-  dependencies:
-    "@semantic-release/error" "^2.2.0"
-    aggregate-error "^3.0.0"
-    execa "^3.2.0"
-    fs-extra "^8.0.0"
-    lodash "^4.17.15"
-    nerf-dart "^1.0.0"
-    normalize-url "^4.0.0"
-    npm "^6.10.3"
-    rc "^1.2.8"
-    read-pkg "^5.0.0"
-    registry-auth-token "^4.0.0"
-    tempy "^0.3.0"
-
-"@semantic-release/release-notes-generator@^7.1.2":
-  version "7.3.2"
-  resolved "https://registry.yarnpkg.com/@semantic-release/release-notes-generator/-/release-notes-generator-7.3.2.tgz#a858b35c9c62f780d285aeaca8ef9891a62c2f9c"
-  integrity sha512-vYGydZPoQqL4aJOsaqXTZIekRb3aa/OlxlEVUvyrWWlNGqmQ1T7NUOos9eoN5DBCEuk6PwDrxPbhzgswxcvprQ==
-  dependencies:
-    conventional-changelog-angular "^5.0.0"
-    conventional-changelog-writer "^4.0.0"
-    conventional-commits-filter "^2.0.0"
-    conventional-commits-parser "^3.0.0"
-    debug "^4.0.0"
-    get-stream "^5.0.0"
-    import-from "^3.0.0"
-    into-stream "^5.0.0"
-    lodash "^4.17.4"
-    read-pkg-up "^7.0.0"
-
 "@sheerun/mutationobserver-shim@^0.3.2":
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b"
@@ -1548,7 +1339,7 @@
     "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0"
     "@svgr/babel-plugin-transform-svg-component" "^4.2.0"
 
-"@svgr/core@^4.1.0":
+"@svgr/core@^4.3.2":
   version "4.3.3"
   resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.3.3.tgz#b37b89d5b757dc66e8c74156d00c368338d24293"
   integrity sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w==
@@ -1564,7 +1355,7 @@
   dependencies:
     "@babel/types" "^7.4.4"
 
-"@svgr/plugin-jsx@^4.1.0", "@svgr/plugin-jsx@^4.3.3":
+"@svgr/plugin-jsx@^4.3.2", "@svgr/plugin-jsx@^4.3.3":
   version "4.3.3"
   resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz#e2ba913dbdfbe85252a34db101abc7ebd50992fa"
   integrity sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w==
@@ -1574,7 +1365,7 @@
     "@svgr/hast-util-to-babel-ast" "^4.3.2"
     svg-parser "^2.0.0"
 
-"@svgr/plugin-svgo@^4.0.3":
+"@svgr/plugin-svgo@^4.3.1":
   version "4.3.1"
   resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz#daac0a3d872e3f55935c6588dd370336865e9e32"
   integrity sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w==
@@ -1583,19 +1374,19 @@
     merge-deep "^3.0.2"
     svgo "^1.2.2"
 
-"@svgr/webpack@4.1.0":
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-4.1.0.tgz#20c88f32f731c7b1d4711045b2b993887d731c28"
-  integrity sha512-d09ehQWqLMywP/PT/5JvXwPskPK9QCXUjiSkAHehreB381qExXf5JFCBWhfEyNonRbkIneCeYM99w+Ud48YIQQ==
+"@svgr/webpack@4.3.2":
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-4.3.2.tgz#319d4471c8f3d5c3af35059274834d9b5b8fb956"
+  integrity sha512-F3VE5OvyOWBEd2bF7BdtFRyI6E9it3mN7teDw0JQTlVtc4HZEYiiLSl+Uf9Uub6IYHVGc+qIrxxDyeedkQru2w==
   dependencies:
-    "@babel/core" "^7.1.6"
+    "@babel/core" "^7.4.5"
     "@babel/plugin-transform-react-constant-elements" "^7.0.0"
-    "@babel/preset-env" "^7.1.6"
+    "@babel/preset-env" "^7.4.5"
     "@babel/preset-react" "^7.0.0"
-    "@svgr/core" "^4.1.0"
-    "@svgr/plugin-jsx" "^4.1.0"
-    "@svgr/plugin-svgo" "^4.0.3"
-    loader-utils "^1.1.0"
+    "@svgr/core" "^4.3.2"
+    "@svgr/plugin-jsx" "^4.3.2"
+    "@svgr/plugin-svgo" "^4.3.1"
+    loader-utils "^1.2.3"
 
 "@testing-library/dom@^6.3.0":
   version "6.10.1"
@@ -1610,9 +1401,9 @@
     wait-for-expect "^3.0.0"
 
 "@testing-library/jest-dom@^4.2.3":
-  version "4.2.3"
-  resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.2.3.tgz#2b40d463d6cb00b5550d04381912e048e8e35966"
-  integrity sha512-lrsm8OMaOLjh8AJhTNZW85Vur+a2U00ej1r/dNzABN4vfJ2kllsP/eLgkOdfCHuspdXn3/Q6rLt/41dSueVCyg==
+  version "4.2.4"
+  resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz#00dfa0cbdd837d9a3c2a7f3f0a248ea6e7b89742"
+  integrity sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==
   dependencies:
     "@babel/runtime" "^7.5.1"
     chalk "^2.4.1"
@@ -1880,25 +1671,16 @@
     "@types/d3-voronoi" "*"
     "@types/d3-zoom" "*"
 
-"@types/events@*":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
-  integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
+"@types/eslint-visitor-keys@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
+  integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
 
 "@types/geojson@*":
   version "7946.0.7"
   resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad"
   integrity sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==
 
-"@types/glob@^7.1.1":
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
-  integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
-  dependencies:
-    "@types/events" "*"
-    "@types/minimatch" "*"
-    "@types/node" "*"
-
 "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
@@ -1919,25 +1701,10 @@
     "@types/istanbul-lib-coverage" "*"
     "@types/istanbul-lib-report" "*"
 
-"@types/lodash@^4.14.132":
-  version "4.14.146"
-  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.146.tgz#de0d2c8610012f12a6a796455054cbc654f8fecf"
-  integrity sha512-JzJcmQ/ikHSv7pbvrVNKJU5j9jL9VLf3/gqs048CEnBVVVEv4kve3vLxoPHGvclutS+Il4SBIuQQ087m1eHffw==
-
-"@types/minimatch@*":
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
-  integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
-
-"@types/node@*", "@types/node@>= 8", "@types/node@^12.0.2":
-  version "12.12.7"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11"
-  integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==
-
-"@types/normalize-package-data@^2.4.0":
-  version "2.4.0"
-  resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
-  integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
+"@types/json-schema@^7.0.3":
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
+  integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
 
 "@types/parse-json@^4.0.0":
   version "4.0.0"
@@ -1961,7 +1728,7 @@
   dependencies:
     "@types/react" "*"
 
-"@types/react@*", "@types/react@^16.9.11":
+"@types/react@*":
   version "16.9.11"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.11.tgz#70e0b7ad79058a7842f25ccf2999807076ada120"
   integrity sha512-UBT4GZ3PokTXSWmdgC/GeCGEJXE5ofWyibCcecRLUVN2ZBpXQGVgQGtG2foS7CrTKFKlQVVswLvf7Js6XA/CVQ==
@@ -1969,11 +1736,6 @@
     "@types/prop-types" "*"
     csstype "^2.2.0"
 
-"@types/retry@^0.12.0":
-  version "0.12.0"
-  resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
-  integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
-
 "@types/stack-utils@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@@ -1994,13 +1756,6 @@
     "@types/react-dom" "*"
     "@types/testing-library__dom" "*"
 
-"@types/victory@^31.0.18":
-  version "31.0.22"
-  resolved "https://registry.yarnpkg.com/@types/victory/-/victory-31.0.22.tgz#b1c0f87261af7bc264207e3864acfe75d0abf605"
-  integrity sha512-6xwSLp6nefiN7/xXk3tscmz2C9JAGgT5z/WtPlHz2uAqstbnyC0EmUXzU/RScZT1odLmHyLhkDRzEezJOzxb5A==
-  dependencies:
-    "@types/react" "*"
-
 "@types/yargs-parser@*":
   version "13.1.0"
   resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228"
@@ -2013,32 +1768,47 @@
   dependencies:
     "@types/yargs-parser" "*"
 
-"@typescript-eslint/eslint-plugin@1.6.0":
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz#a5ff3128c692393fb16efa403ec7c8a5593dab0f"
-  integrity sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==
+"@typescript-eslint/eslint-plugin@^2.2.0":
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.7.0.tgz#dff176bdb73dfd7e2e43062452189bd1b9db6021"
+  integrity sha512-H5G7yi0b0FgmqaEUpzyBlVh0d9lq4cWG2ap0RKa6BkF3rpBb6IrAoubt1NWh9R2kRs/f0k6XwRDiDz3X/FqXhQ==
   dependencies:
-    "@typescript-eslint/parser" "1.6.0"
-    "@typescript-eslint/typescript-estree" "1.6.0"
-    requireindex "^1.2.0"
-    tsutils "^3.7.0"
+    "@typescript-eslint/experimental-utils" "2.7.0"
+    eslint-utils "^1.4.2"
+    functional-red-black-tree "^1.0.1"
+    regexpp "^2.0.1"
+    tsutils "^3.17.1"
 
-"@typescript-eslint/parser@1.6.0":
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.6.0.tgz#f01189c8b90848e3b8e45a6cdad27870529d1804"
-  integrity sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==
+"@typescript-eslint/experimental-utils@2.7.0":
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.7.0.tgz#58d790a3884df3041b5a5e08f9e5e6b7c41864b5"
+  integrity sha512-9/L/OJh2a5G2ltgBWJpHRfGnt61AgDeH6rsdg59BH0naQseSwR7abwHq3D5/op0KYD/zFT4LS5gGvWcMmegTEg==
   dependencies:
-    "@typescript-eslint/typescript-estree" "1.6.0"
-    eslint-scope "^4.0.0"
-    eslint-visitor-keys "^1.0.0"
+    "@types/json-schema" "^7.0.3"
+    "@typescript-eslint/typescript-estree" "2.7.0"
+    eslint-scope "^5.0.0"
 
-"@typescript-eslint/typescript-estree@1.6.0":
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz#6cf43a07fee08b8eb52e4513b428c8cdc9751ef0"
-  integrity sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==
+"@typescript-eslint/parser@^2.2.0":
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.7.0.tgz#b5e6a4944e2b68dba1e7fbfd5242e09ff552fd12"
+  integrity sha512-ctC0g0ZvYclxMh/xI+tyqP0EC2fAo6KicN9Wm2EIao+8OppLfxji7KAGJosQHSGBj3TcqUrA96AjgXuKa5ob2g==
+  dependencies:
+    "@types/eslint-visitor-keys" "^1.0.0"
+    "@typescript-eslint/experimental-utils" "2.7.0"
+    "@typescript-eslint/typescript-estree" "2.7.0"
+    eslint-visitor-keys "^1.1.0"
+
+"@typescript-eslint/typescript-estree@2.7.0":
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.7.0.tgz#34fd98c77a07b40d04d5b4203eddd3abeab909f4"
+  integrity sha512-vVCE/DY72N4RiJ/2f10PTyYekX2OLaltuSIBqeHYI44GQ940VCYioInIb8jKMrK9u855OEJdFC+HmWAZTnC+Ag==
   dependencies:
+    debug "^4.1.1"
+    glob "^7.1.4"
+    is-glob "^4.0.1"
     lodash.unescape "4.0.1"
-    semver "5.5.0"
+    semver "^6.3.0"
+    tsutils "^3.17.1"
 
 "@webassemblyjs/ast@1.8.5":
   version "1.8.5"
@@ -2196,20 +1966,12 @@
   resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
   integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
 
-JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5:
-  version "1.3.5"
-  resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
-  integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
-  dependencies:
-    jsonparse "^1.2.0"
-    through ">=2.2.7 <3"
-
 abab@^2.0.0:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a"
   integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==
 
-abbrev@1, abbrev@~1.1.1:
+abbrev@1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
   integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
@@ -2222,11 +1984,6 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
     mime-types "~2.1.24"
     negotiator "0.6.2"
 
-acorn-dynamic-import@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948"
-  integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==
-
 acorn-globals@^4.1.0, acorn-globals@^4.3.0, acorn-globals@^4.3.2:
   version "4.3.4"
   resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7"
@@ -2235,7 +1992,7 @@ acorn-globals@^4.1.0, acorn-globals@^4.3.0, acorn-globals@^4.3.2:
     acorn "^6.0.1"
     acorn-walk "^6.0.1"
 
-acorn-jsx@^5.0.0:
+acorn-jsx@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384"
   integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==
@@ -2250,7 +2007,7 @@ acorn@^5.5.3:
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
   integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
 
-acorn@^6.0.1, acorn@^6.0.4, acorn@^6.0.5, acorn@^6.0.7:
+acorn@^6.0.1, acorn@^6.0.4, acorn@^6.2.1:
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
   integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
@@ -2265,46 +2022,28 @@ address@1.1.2, address@^1.0.1:
   resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
   integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
 
-agent-base@4, agent-base@^4.3.0:
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
-  integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
-  dependencies:
-    es6-promisify "^5.0.0"
-
-agent-base@~4.2.1:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
-  integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
-  dependencies:
-    es6-promisify "^5.0.0"
-
-agentkeepalive@^3.4.1:
-  version "3.5.2"
-  resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67"
-  integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==
-  dependencies:
-    humanize-ms "^1.2.1"
-
-aggregate-error@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0"
-  integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==
+adjust-sourcemap-loader@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4"
+  integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA==
   dependencies:
-    clean-stack "^2.0.0"
-    indent-string "^4.0.0"
+    assert "1.4.1"
+    camelcase "5.0.0"
+    loader-utils "1.2.3"
+    object-path "0.11.4"
+    regex-parser "2.2.10"
 
 ajv-errors@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
   integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
 
-ajv-keywords@^3.1.0:
+ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
   version "3.4.1"
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
   integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
 
-ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5:
   version "6.10.2"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
   integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
@@ -2319,13 +2058,6 @@ alphanum-sort@^1.0.0:
   resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
   integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
 
-ansi-align@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
-  integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=
-  dependencies:
-    string-width "^2.0.0"
-
 ansi-colors@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9"
@@ -2345,11 +2077,18 @@ ansi-cyan@^0.1.1:
   dependencies:
     ansi-wrap "0.1.0"
 
-ansi-escapes@^3.0.0, ansi-escapes@^3.1.0, ansi-escapes@^3.2.0:
+ansi-escapes@^3.0.0, ansi-escapes@^3.2.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
   integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
 
+ansi-escapes@^4.2.1:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d"
+  integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==
+  dependencies:
+    type-fest "^0.8.1"
+
 ansi-gray@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251"
@@ -2384,6 +2123,11 @@ ansi-regex@^4.0.0, ansi-regex@^4.1.0:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
   integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
 
+ansi-regex@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
+  integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -2401,16 +2145,6 @@ ansi-wrap@0.1.0, ansi-wrap@^0.1.0:
   resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
   integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768=
 
-ansicolors@~0.3.2:
-  version "0.3.2"
-  resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
-  integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=
-
-ansistyles@~0.1.3:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz#5de60415bda071bb37127854c864f41b23254539"
-  integrity sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=
-
 anymatch@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -2419,21 +2153,11 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
-aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2:
+aproba@^1.0.3, aproba@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
   integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
 
-"aproba@^1.1.2 || 2", aproba@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
-  integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
-
-archy@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
-  integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=
-
 are-we-there-yet@~1.1.2:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
@@ -2449,11 +2173,6 @@ argparse@^1.0.7:
   dependencies:
     sprintf-js "~1.0.2"
 
-argv-formatter@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/argv-formatter/-/argv-formatter-1.0.0.tgz#a0ca0cbc29a5b73e836eebe1cbf6c5e0e4eb82f9"
-  integrity sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=
-
 aria-query@3.0.0, aria-query@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
@@ -2462,6 +2181,11 @@ aria-query@3.0.0, aria-query@^3.0.0:
     ast-types-flow "0.0.7"
     commander "^2.11.0"
 
+arity-n@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745"
+  integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U=
+
 arr-diff@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a"
@@ -2495,11 +2219,6 @@ array-equal@^1.0.0:
   resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
   integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
 
-array-find-index@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
-  integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
-
 array-flatten@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@@ -2510,11 +2229,6 @@ array-flatten@^2.1.0:
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
   integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
 
-array-ify@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece"
-  integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=
-
 array-includes@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
@@ -2535,21 +2249,11 @@ array-union@^1.0.1:
   dependencies:
     array-uniq "^1.0.1"
 
-array-union@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
-  integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-
 array-uniq@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
   integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
 
-array-uniq@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-2.1.0.tgz#46603d5e28e79bfd02b046fcc1d77c6820bd8e98"
-  integrity sha512-bdHxtev7FN6+MXI1YFW0Q8mQ8dTJc2S8AMfju+ZR77pbg2yAdVyDlwkaUI7Har0LyOMRFPHrJ9lYdyjZZswdlQ==
-
 array-unique@^0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
@@ -2560,7 +2264,7 @@ arrify@^1.0.1:
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
   integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
 
-asap@^2.0.0, asap@~2.0.3, asap@~2.0.6:
+asap@~2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
@@ -2586,6 +2290,13 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
   resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
   integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
 
+assert@1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
+  integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=
+  dependencies:
+    util "0.10.3"
+
 assert@^1.1.1:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
@@ -2631,17 +2342,12 @@ asynckit@^0.4.0:
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
   integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
 
-atob-lite@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696"
-  integrity sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=
-
 atob@^2.1.1:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
   integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
 
-autoprefixer@^9.4.9:
+autoprefixer@^9.6.1:
   version "9.7.1"
   resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.1.tgz#9ffc44c55f5ca89253d9bb7186cefb01ef57747f"
   integrity sha512-w3b5y1PXWlhYulevrTJ0lizkQ5CyqfeU6BIRDbuhsMupstHQOeb1Ur80tcB1zxSu7AwyY/qCQ7Vvqklh31ZBFw==
@@ -2710,17 +2416,17 @@ babel-core@^6.26.0, babel-core@^6.7.2:
     slash "^1.0.0"
     source-map "^0.5.7"
 
-babel-eslint@10.0.1:
-  version "10.0.1"
-  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
-  integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==
+babel-eslint@10.0.3:
+  version "10.0.3"
+  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a"
+  integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==
   dependencies:
     "@babel/code-frame" "^7.0.0"
     "@babel/parser" "^7.0.0"
     "@babel/traverse" "^7.0.0"
     "@babel/types" "^7.0.0"
-    eslint-scope "3.7.1"
     eslint-visitor-keys "^1.0.0"
+    resolve "^1.12.0"
 
 babel-eslint@^9.0.0:
   version "9.0.0"
@@ -2839,7 +2545,7 @@ babel-helpers@^6.24.1:
     babel-runtime "^6.22.0"
     babel-template "^6.24.1"
 
-babel-jest@^24.8.0, babel-jest@^24.9.0:
+babel-jest@^24.9.0:
   version "24.9.0"
   resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54"
   integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==
@@ -2852,15 +2558,15 @@ babel-jest@^24.8.0, babel-jest@^24.9.0:
     chalk "^2.4.2"
     slash "^2.0.0"
 
-babel-loader@8.0.5:
-  version "8.0.5"
-  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.5.tgz#225322d7509c2157655840bba52e46b6c2f2fe33"
-  integrity sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw==
+babel-loader@8.0.6:
+  version "8.0.6"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb"
+  integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==
   dependencies:
     find-cache-dir "^2.0.0"
     loader-utils "^1.0.2"
     mkdirp "^0.5.1"
-    util.promisify "^1.0.0"
+    pify "^4.0.1"
 
 babel-messages@^6.23.0:
   version "6.23.0"
@@ -2918,7 +2624,7 @@ babel-plugin-jest-hoist@^24.9.0:
   dependencies:
     "@types/babel__traverse" "^7.0.6"
 
-babel-plugin-macros@2.6.1, babel-plugin-macros@^2.0.0:
+babel-plugin-macros@2.6.1:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.6.1.tgz#41f7ead616fc36f6a93180e89697f69f51671181"
   integrity sha512-6W2nwiXme6j1n2erPOnmRiWfObUhWH7Qw1LMi9XZy8cj+KtESu3T6asZvtk5bMQQjX8te35o7CFueiSdL/2NmQ==
@@ -2927,7 +2633,16 @@ babel-plugin-macros@2.6.1, babel-plugin-macros@^2.0.0:
     cosmiconfig "^5.2.0"
     resolve "^1.10.0"
 
-babel-plugin-named-asset-import@^0.3.2:
+babel-plugin-macros@^2.0.0:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.6.2.tgz#98ae30a02645dfa8033628fe613854ec9541bbc8"
+  integrity sha512-Ntviq8paRTkXIxvrJBauib+2KqQbZQuh4593CEZFF8qz3IVP8VituTZmkGe6N7rsuiOIbejxXj6kx3LMlEq0UA==
+  dependencies:
+    "@babel/runtime" "^7.7.2"
+    cosmiconfig "^6.0.0"
+    resolve "^1.12.0"
+
+babel-plugin-named-asset-import@^0.3.4:
   version "0.3.4"
   resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.4.tgz#4a8fc30e9a3e2b1f5ed36883386ab2d84e1089bd"
   integrity sha512-S6d+tEzc5Af1tKIMbsf2QirCcPdQ+mKUCY2H1nJj1DyA1ShwpsoxEOAwbWsG5gcXNV/olpvQd9vrUWRx4bnhpw==
@@ -3198,7 +2913,7 @@ babel-preset-jest@^24.9.0:
     "@babel/plugin-syntax-object-rest-spread" "^7.0.0"
     babel-plugin-jest-hoist "^24.9.0"
 
-babel-preset-react-app@^9.0.0:
+babel-preset-react-app@^9.0.2:
   version "9.0.2"
   resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-9.0.2.tgz#247d37e883d6d6f4b4691e5f23711bb2dd80567d"
   integrity sha512-aXD+CTH8Chn8sNJr4tO/trWKqe5sSE4hdO76j9fhVezJSzmpWYWUSc5JoPmdSxADwef5kQFNGKXd433vvkd2VQ==
@@ -3233,7 +2948,7 @@ babel-register@^6.26.0:
     mkdirp "^0.5.1"
     source-map-support "^0.4.15"
 
-babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0:
+babel-runtime@^6.22.0, babel-runtime@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
   integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
@@ -3317,33 +3032,17 @@ bcrypt-pbkdf@^1.0.0:
   dependencies:
     tweetnacl "^0.14.3"
 
-before-after-hook@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635"
-  integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==
-
 big.js@^5.2.2:
   version "5.2.2"
   resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
   integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
 
-bin-links@^1.1.2, bin-links@^1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-1.1.3.tgz#702fd59552703727313bc624bdbc4c0d3431c2ca"
-  integrity sha512-TEwmH4PHU/D009stP+fkkazMJgkBNCv60z01lQ/Mn8E6+ThHoD03svMnBVuCowwXo2nP2qKyKZxKxp58OHRzxw==
-  dependencies:
-    bluebird "^3.5.3"
-    cmd-shim "^3.0.0"
-    gentle-fs "^2.0.1"
-    graceful-fs "^4.1.15"
-    write-file-atomic "^2.3.0"
-
 binary-extensions@^1.0.0:
   version "1.13.1"
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
   integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
 
-bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5:
+bluebird@^3.5.5:
   version "3.7.1"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de"
   integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==
@@ -3405,11 +3104,6 @@ bootstrap-select@1.12.2:
   dependencies:
     jquery ">=1.8"
 
-bootstrap-slider-without-jquery@^10.0.0:
-  version "10.0.0"
-  resolved "https://registry.yarnpkg.com/bootstrap-slider-without-jquery/-/bootstrap-slider-without-jquery-10.0.0.tgz#5c304461b3b915037c7c118806c8ca08102f5de3"
-  integrity sha512-CB9CrpNVrIytlOoqHtRXhhxFo/jencr1U5cMqPBA0WmMdb13bzjHnXQVNGYde/g5gWW+RWiuT9jTquZuz3VE8A==
-
 bootstrap-slider@^9.9.0:
   version "9.10.0"
   resolved "https://registry.yarnpkg.com/bootstrap-slider/-/bootstrap-slider-9.10.0.tgz#1103d6bc00cfbfa8cfc9a2599ab518c55643da3f"
@@ -3430,24 +3124,6 @@ bootstrap@^3.3, bootstrap@^3.4.1, bootstrap@~3.4.1:
   resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.4.1.tgz#c3a347d419e289ad11f4033e3c4132b87c081d72"
   integrity sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==
 
-bottleneck@^2.18.1:
-  version "2.19.5"
-  resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91"
-  integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==
-
-boxen@^1.2.1:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
-  integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==
-  dependencies:
-    ansi-align "^2.0.0"
-    camelcase "^4.0.0"
-    chalk "^2.0.1"
-    cli-boxes "^1.0.0"
-    string-width "^2.0.0"
-    term-size "^1.2.0"
-    widest-line "^2.0.0"
-
 brace-expansion@^1.1.7:
   version "1.1.11"
   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -3472,18 +3148,6 @@ braces@^2.3.1, braces@^2.3.2:
     split-string "^3.0.2"
     to-regex "^3.0.1"
 
-braces@^3.0.1:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
-  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
-  dependencies:
-    fill-range "^7.0.1"
-
-breakjs@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/breakjs/-/breakjs-1.0.0.tgz#ec8353a06862eb43962deae09072ee66a4cd8459"
-  integrity sha1-7INToGhi60OWLergkHLuZqTNhFk=
-
 brorand@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -3569,7 +3233,7 @@ browserslist@4.7.0:
     electron-to-chromium "^1.3.247"
     node-releases "^1.1.29"
 
-browserslist@^4.0.0, browserslist@^4.1.1, browserslist@^4.4.2, browserslist@^4.6.0, browserslist@^4.7.2:
+browserslist@^4.0.0, browserslist@^4.1.1, browserslist@^4.6.0, browserslist@^4.6.4, browserslist@^4.7.2:
   version "4.7.2"
   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.2.tgz#1bb984531a476b5d389cedecb195b2cd69fb1348"
   integrity sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw==
@@ -3585,11 +3249,6 @@ bser@^2.0.0:
   dependencies:
     node-int64 "^0.4.0"
 
-btoa-lite@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
-  integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc=
-
 buffer-from@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@@ -3624,21 +3283,6 @@ builtin-status-codes@^3.0.0:
   resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
   integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
 
-builtins@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
-  integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og=
-
-byline@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
-  integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=
-
-byte-size@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191"
-  integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==
-
 bytes@3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
@@ -3649,34 +3293,14 @@ bytes@3.1.0:
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
   integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
 
-c3@^0.4.11, c3@~0.4.11:
-  version "0.4.23"
-  resolved "https://registry.yarnpkg.com/c3/-/c3-0.4.23.tgz#32ece135d0ac6d124187be5c6935903699643002"
-  integrity sha512-fI6hbx1QoATU0gRQtPWsUGWX+ssXhxGH1ogew32KjVmGHFE4WmfmBkh+RkuHDoeCIoGFon7XTpKcwUZpBGW4mQ==
+c3@~0.4.11:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/c3/-/c3-0.4.24.tgz#57b62357098842d38e265a265f6de1e8c6faadd2"
+  integrity sha512-mVCFtN5ZWUT5UE7ilFQ7KBQ7TUCdKIq6KsDt1hH/1m6gC1tBjvzFTO7fqhaiWHfhNOjjM7makschdhg6DkWQMA==
   dependencies:
     d3 "~3.5.0"
 
-cacache@^11.0.2:
-  version "11.3.3"
-  resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.3.tgz#8bd29df8c6a718a6ebd2d010da4d7972ae3bbadc"
-  integrity sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==
-  dependencies:
-    bluebird "^3.5.5"
-    chownr "^1.1.1"
-    figgy-pudding "^3.5.1"
-    glob "^7.1.4"
-    graceful-fs "^4.1.15"
-    lru-cache "^5.1.1"
-    mississippi "^3.0.0"
-    mkdirp "^0.5.1"
-    move-concurrently "^1.0.1"
-    promise-inflight "^1.0.1"
-    rimraf "^2.6.3"
-    ssri "^6.0.1"
-    unique-filename "^1.1.1"
-    y18n "^4.0.0"
-
-cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3:
+cacache@^12.0.2:
   version "12.0.3"
   resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390"
   integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==
@@ -3712,16 +3336,6 @@ cache-base@^1.0.1:
     union-value "^1.0.0"
     unset-value "^1.0.0"
 
-cachedir@2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.2.0.tgz#19afa4305e05d79e417566882e0c8f960f62ff0e"
-  integrity sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ==
-
-call-limit@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/call-limit/-/call-limit-1.1.1.tgz#ef15f2670db3f1992557e2d965abc459e6e358d4"
-  integrity sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==
-
 call-me-maybe@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
@@ -3759,16 +3373,12 @@ camel-case@3.0.x, camel-case@^3.0.0:
     no-case "^2.2.0"
     upper-case "^1.1.1"
 
-camelcase-keys@^4.0.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77"
-  integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=
-  dependencies:
-    camelcase "^4.1.0"
-    map-obj "^2.0.0"
-    quick-lru "^1.0.0"
+camelcase@5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
+  integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
 
-camelcase@^4.0.0, camelcase@^4.1.0:
+camelcase@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
   integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
@@ -3788,10 +3398,10 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000939, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001004, caniuse-lite@^1.0.30001006:
-  version "1.0.30001009"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001009.tgz#69b77997b882a7aee6af24c8d7d2fa27ee41f348"
-  integrity sha512-M3rEqHN6SaVjgo4bIik7HsGcWXsi+lI9WA0p51RPMFx5gXfduyOXWJrc0R4xBkSK1pgNf4CNgy5M+6H+WiEP8g==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001004, caniuse-lite@^1.0.30001006:
+  version "1.0.30001010"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001010.tgz#397a14034d384260453cc81994f494626d34b938"
+  integrity sha512-RA5GH9YjFNea4ZQszdWgh2SC+dpLiRAg4VDQS2b5JRI45OxmbGrYocYHTa9x0bKMQUE7uvHkNPNffUr+pCxSGw==
 
 capture-exit@^2.0.0:
   version "2.0.0"
@@ -3800,19 +3410,6 @@ capture-exit@^2.0.0:
   dependencies:
     rsvp "^4.8.4"
 
-capture-stack-trace@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
-  integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==
-
-cardinal@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505"
-  integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU=
-  dependencies:
-    ansicolors "~0.3.2"
-    redeyed "~2.1.0"
-
 case-sensitive-paths-webpack-plugin@2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.2.0.tgz#3371ef6365ef9c25fa4b81c16ace0e9c7dc58c3e"
@@ -3823,7 +3420,7 @@ caseless@~0.12.0:
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
   integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
 
-chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2:
+chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -3843,11 +3440,6 @@ chalk@^1.1.3:
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
-change-emitter@^0.1.2:
-  version "0.1.6"
-  resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
-  integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=
-
 chardet@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -3872,35 +3464,23 @@ chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4:
   optionalDependencies:
     fsevents "^1.2.7"
 
-chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.3:
+chownr@^1.1.1:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142"
   integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==
 
-chrome-trace-event@^1.0.0:
+chrome-trace-event@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
   integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==
   dependencies:
     tslib "^1.9.0"
 
-ci-info@^1.5.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
-  integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==
-
 ci-info@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
   integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
 
-cidr-regex@^2.0.10:
-  version "2.0.10"
-  resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-2.0.10.tgz#af13878bd4ad704de77d6dc800799358b3afa70d"
-  integrity sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==
-  dependencies:
-    ip-regex "^2.1.0"
-
 cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
@@ -3919,7 +3499,7 @@ class-utils@^0.3.5:
     isobject "^3.0.0"
     static-extend "^0.1.1"
 
-classnames@^2.2.0, classnames@^2.2.5:
+classnames@^2.2.5:
   version "2.2.6"
   resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
   integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
@@ -3931,24 +3511,6 @@ clean-css@4.2.x:
   dependencies:
     source-map "~0.6.0"
 
-clean-stack@^2.0.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
-  integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-
-cli-boxes@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
-  integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM=
-
-cli-columns@^3.1.2:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/cli-columns/-/cli-columns-3.1.2.tgz#6732d972979efc2ae444a1f08e08fa139c96a18e"
-  integrity sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=
-  dependencies:
-    string-width "^2.0.0"
-    strip-ansi "^3.0.1"
-
 cli-cursor@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
@@ -3956,22 +3518,12 @@ cli-cursor@^2.1.0:
   dependencies:
     restore-cursor "^2.0.0"
 
-cli-table3@^0.5.0, cli-table3@^0.5.1:
-  version "0.5.1"
-  resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202"
-  integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==
-  dependencies:
-    object-assign "^4.1.0"
-    string-width "^2.1.1"
-  optionalDependencies:
-    colors "^1.1.2"
-
-cli-table@^0.3.1:
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
-  integrity sha1-9TsFJmqLGguTSz0IIebi3FkUriM=
+cli-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+  integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
   dependencies:
-    colors "1.0.3"
+    restore-cursor "^3.1.0"
 
 cli-width@^2.0.0:
   version "2.2.0"
@@ -4007,28 +3559,14 @@ clone-deep@^0.2.4:
     lazy-cache "^1.0.3"
     shallow-clone "^0.1.2"
 
-clone-deep@^2.0.1:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713"
-  integrity sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==
+clone-deep@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+  integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
   dependencies:
-    for-own "^1.0.0"
     is-plain-object "^2.0.4"
-    kind-of "^6.0.0"
-    shallow-clone "^1.0.0"
-
-clone@^1.0.2:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
-  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
-
-cmd-shim@^3.0.0, cmd-shim@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-3.0.3.tgz#2c35238d3df37d98ecdd7d5f6b8dc6b21cadc7cb"
-  integrity sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==
-  dependencies:
-    graceful-fs "^4.1.2"
-    mkdirp "~0.5.0"
+    kind-of "^6.0.2"
+    shallow-clone "^3.0.0"
 
 co@^4.6.0:
   version "4.6.0"
@@ -4095,24 +3633,6 @@ color@^3.0.0:
     color-convert "^1.9.1"
     color-string "^1.5.2"
 
-colors@1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
-  integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
-
-colors@^1.1.2:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
-  integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
-
-columnify@~1.5.4:
-  version "1.5.4"
-  resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb"
-  integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=
-  dependencies:
-    strip-ansi "^3.0.0"
-    wcwidth "^1.0.0"
-
 combined-stream@^1.0.6, combined-stream@~1.0.6:
   version "1.0.8"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -4120,7 +3640,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@2, commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3:
+commander@2, commander@^2.11.0, commander@^2.20.0, commander@~2.20.3:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
   integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -4135,27 +3655,6 @@ commander@~2.19.0:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
   integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
 
-commitizen@^4.0.3:
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.0.3.tgz#c19a4213257d0525b85139e2f36db7cc3b4f6dae"
-  integrity sha512-lxu0F/Iq4dudoFeIl5pY3h3CQJzkmQuh3ygnaOvqhAD8Wu2pYBI17ofqSuPHNsBTEOh1r1AVa9kR4Hp0FAHKcQ==
-  dependencies:
-    cachedir "2.2.0"
-    cz-conventional-changelog "3.0.1"
-    dedent "0.7.0"
-    detect-indent "6.0.0"
-    find-node-modules "2.0.0"
-    find-root "1.1.0"
-    fs-extra "8.1.0"
-    glob "7.1.4"
-    inquirer "6.5.0"
-    is-utf8 "^0.2.1"
-    lodash "4.17.15"
-    minimist "1.2.0"
-    shelljs "0.7.6"
-    strip-bom "4.0.0"
-    strip-json-comments "3.0.1"
-
 common-tags@^1.8.0:
   version "1.8.0"
   resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
@@ -4166,19 +3665,18 @@ commondir@^1.0.1:
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
   integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
 
-compare-func@^1.3.1:
-  version "1.3.2"
-  resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648"
-  integrity sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=
-  dependencies:
-    array-ify "^1.0.0"
-    dot-prop "^3.0.0"
-
 component-emitter@^1.2.1:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
   integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
 
+compose-function@3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f"
+  integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=
+  dependencies:
+    arity-n "^1.0.4"
+
 compressible@~2.0.16:
   version "2.0.17"
   resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
@@ -4214,27 +3712,7 @@ concat-stream@^1.5.0:
     readable-stream "^2.2.2"
     typedarray "^0.0.6"
 
-config-chain@^1.1.12:
-  version "1.1.12"
-  resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
-  integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
-  dependencies:
-    ini "^1.3.4"
-    proto-list "~1.2.1"
-
-configstore@^3.0.0:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f"
-  integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==
-  dependencies:
-    dot-prop "^4.1.0"
-    graceful-fs "^4.1.2"
-    make-dir "^1.0.0"
-    unique-string "^1.0.0"
-    write-file-atomic "^2.0.0"
-    xdg-basedir "^3.0.0"
-
-confusing-browser-globals@^1.0.7:
+confusing-browser-globals@^1.0.9:
   version "1.0.9"
   resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd"
   integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==
@@ -4249,7 +3727,7 @@ console-browserify@^1.1.0:
   resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
   integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
 
-console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0:
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
   integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
@@ -4276,55 +3754,17 @@ content-type@~1.0.4:
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
   integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
 
-conventional-changelog-angular@^5.0.0:
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz#269540c624553aded809c29a3508fdc2b544c059"
-  integrity sha512-QDEmLa+7qdhVIv8sFZfVxU1VSyVvnXPsxq8Vam49mKUcO1Z8VTLEJk9uI21uiJUsnmm0I4Hrsdc9TgkOQo9WSA==
-  dependencies:
-    compare-func "^1.3.1"
-    q "^1.5.1"
-
-conventional-changelog-writer@^4.0.0:
-  version "4.0.10"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.10.tgz#39f6458cca62a8151b3ce582a57ff71fd2b0ff7a"
-  integrity sha512-vtO9vBAVh7XnSpGLTB1BOGgsGTz1MdvFjzbSXLrtapWCHWwuVOZFgwdLhlS0MaXwlF1dksWdEb6tnr42Ie2INw==
-  dependencies:
-    compare-func "^1.3.1"
-    conventional-commits-filter "^2.0.2"
-    dateformat "^3.0.0"
-    handlebars "^4.4.0"
-    json-stringify-safe "^5.0.1"
-    lodash "^4.17.15"
-    meow "^4.0.0"
-    semver "^6.0.0"
-    split "^1.0.0"
-    through2 "^3.0.0"
-
-conventional-commit-types@^2.0.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/conventional-commit-types/-/conventional-commit-types-2.3.0.tgz#bc3c8ebba0a9e4b3ecc548f1d0674e251ab8be22"
-  integrity sha512-6iB39PrcGYdz0n3z31kj6/Km6mK9hm9oMRhwcLnKxE7WNoeRKZbTAobliKrbYZ5jqyCvtcVEfjCiaEzhL3AVmQ==
-
-conventional-commits-filter@^2.0.0, conventional-commits-filter@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz#f122f89fbcd5bb81e2af2fcac0254d062d1039c1"
-  integrity sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==
+convert-source-map@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
+  integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
   dependencies:
-    lodash.ismatch "^4.4.0"
-    modify-values "^1.0.0"
+    safe-buffer "~5.1.1"
 
-conventional-commits-parser@^3.0.0, conventional-commits-parser@^3.0.7:
-  version "3.0.7"
-  resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.7.tgz#55b6cde6a2d0b4a7ab399392777d527134a8d05c"
-  integrity sha512-4mx/FRC92z0yIiXGyRVYQFhn0jWDwvxnj2UuLaUi3hJSG4Thall6GXA8YOPHQK2qvotciJandJIVmuSvLgDLbQ==
-  dependencies:
-    JSONStream "^1.0.4"
-    is-text-path "^1.0.1"
-    lodash "^4.17.15"
-    meow "^4.0.0"
-    split2 "^2.0.0"
-    through2 "^3.0.0"
-    trim-off-newlines "^1.0.0"
+convert-source-map@^0.3.3:
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
+  integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA=
 
 convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0:
   version "1.7.0"
@@ -4373,12 +3813,7 @@ core-js@3.2.1:
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.2.1.tgz#cd41f38534da6cc59f7db050fe67307de9868b09"
   integrity sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==
 
-core-js@^1.0.0:
-  version "1.2.7"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
-  integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
-
-core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.6.5:
+core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0:
   version "2.6.10"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f"
   integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==
@@ -4439,13 +3874,6 @@ create-emotion@^9.2.12:
     stylis "^3.5.0"
     stylis-rule-sheet "^0.0.10"
 
-create-error-class@^3.0.0:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
-  integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=
-  dependencies:
-    capture-stack-trace "^1.0.0"
-
 create-hash@^1.1.0, create-hash@^1.1.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
@@ -4469,14 +3897,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
-create-react-context@^0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c"
-  integrity sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==
-  dependencies:
-    gud "^1.0.0"
-    warning "^4.0.3"
-
 cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -4488,7 +3908,7 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
     shebang-command "^1.2.0"
     which "^1.2.9"
 
-cross-spawn@^5.0.1, cross-spawn@^5.1.0:
+cross-spawn@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
   integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
@@ -4497,15 +3917,6 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0:
     shebang-command "^1.2.0"
     which "^1.2.9"
 
-cross-spawn@^7.0.0:
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
-  integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==
-  dependencies:
-    path-key "^3.1.0"
-    shebang-command "^2.0.0"
-    which "^2.0.1"
-
 crypto-browserify@^3.11.0:
   version "3.12.0"
   resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -4523,11 +3934,6 @@ crypto-browserify@^3.11.0:
     randombytes "^2.0.0"
     randomfill "^1.0.3"
 
-crypto-random-string@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
-  integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
-
 css-blank-pseudo@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5"
@@ -4548,11 +3954,6 @@ css-declaration-sorter@^4.0.1:
     postcss "^7.0.1"
     timsort "^0.3.0"
 
-css-element-queries@^1.0.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/css-element-queries/-/css-element-queries-1.2.1.tgz#70d1a0f676fc0bd0a3306522a5b2d3bcc55c9fe6"
-  integrity sha512-hiI1tSzf+U/gE13qhfwnCvN90Ay0THnE+mT3pjN/c/mvFmEUHZVNrvMJrrkw2ppOzkl69FdgH2ZGZENYQUaN2A==
-
 css-has-pseudo@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee"
@@ -4638,7 +4039,7 @@ css.escape@^1.5.1:
   resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
   integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
 
-css@^2.2.3:
+css@^2.0.0, css@^2.2.3:
   version "2.2.4"
   resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
   integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
@@ -4648,7 +4049,7 @@ css@^2.2.3:
     source-map-resolve "^0.5.2"
     urix "^0.1.0"
 
-cssdb@^4.3.0:
+cssdb@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0"
   integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==
@@ -4721,7 +4122,7 @@ cssnano-util-same-parent@^4.0.0:
   resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3"
   integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==
 
-cssnano@^4.1.0:
+cssnano@^4.1.10:
   version "4.1.10"
   resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2"
   integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==
@@ -4744,9 +4145,9 @@ cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@^0.3.4, cssom@~0.3.6:
   integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
 
 cssom@^0.4.1:
-  version "0.4.2"
-  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.2.tgz#e2abd06e1267a7f2e5eccd7770c9ebe1bd88648b"
-  integrity sha512-fVXFVBr7JPDcgqa92UNr6HIpeMypyG/XVloB+512KH43Z2aum8ZNVzRapWR4mZ/f2UlRMymIoDO3aFJmQ6Y3RA==
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
+  integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==
 
 cssstyle@^0.3.1:
   version "0.3.1"
@@ -4774,47 +4175,11 @@ csstype@^2.2.0, csstype@^2.5.2:
   resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5"
   integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==
 
-currently-unhandled@^0.4.1:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
-  integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
-  dependencies:
-    array-find-index "^1.0.1"
-
 cyclist@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
   integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
 
-cz-conventional-changelog@3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.0.1.tgz#b1f207ae050355e7ada65aad5c52e9de3d0c8e5b"
-  integrity sha512-7KASIwB8/ClEyCRvQrCPbN7WkQnUSjSSVNyPM+gDJ0jskLi8h8N2hrdpyeCk7fIqKMRzziqVSOBTB8yyLTMHGQ==
-  dependencies:
-    chalk "^2.4.1"
-    conventional-commit-types "^2.0.0"
-    lodash.map "^4.5.1"
-    longest "^2.0.1"
-    right-pad "^1.0.1"
-    word-wrap "^1.0.3"
-  optionalDependencies:
-    "@commitlint/load" ">6.1.1"
-
-cz-conventional-changelog@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.0.2.tgz#f6b9a406177ab07f9a3a087e06103a045b376260"
-  integrity sha512-MPxERbtQyVp0nnpCBiwzKGKmMBSswmCV3Jpef3Axqd5f3c/SOc6VFiSUlclOyZXBn3Xtf4snzt4O15hBTRb2gA==
-  dependencies:
-    chalk "^2.4.1"
-    commitizen "^4.0.3"
-    conventional-commit-types "^2.0.0"
-    lodash.map "^4.5.1"
-    longest "^2.0.1"
-    right-pad "^1.0.1"
-    word-wrap "^1.0.3"
-  optionalDependencies:
-    "@commitlint/load" ">6.1.1"
-
 d3-array@^1.2.0:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
@@ -4831,26 +4196,26 @@ d3-color@1:
   integrity sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg==
 
 d3-ease@^1.0.0:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.5.tgz#8ce59276d81241b1b72042d6af2d40e76d936ffb"
-  integrity sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.6.tgz#ebdb6da22dfac0a22222f2d4da06f66c416a0ec0"
+  integrity sha512-SZ/lVU7LRXafqp7XtIcBdxnWl8yyLpgOmzAk0mWBI9gXNzLDx5ybZgnRbH9dN/yY5tzVBqCQ9avltSnqVwessQ==
 
 d3-format@1:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.1.tgz#c45f74b17c5a290c072a4ba7039dd19662cd5ce6"
-  integrity sha512-TUswGe6hfguUX1CtKxyG2nymO+1lyThbkS1ifLX0Sr+dOQtAD5gkrffpHnx+yHNKUZ0Bmg5T4AjUQwugPDrm0g==
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.2.tgz#2a8c0ebf500f315981c2110eaaf70b82f472cb2b"
+  integrity sha512-gco1Ih54PgMsyIXgttLxEhNy/mXxq8+rLnCb5shQk+P5TsiySrwWU5gpB4zen626J4LIwBxHvDChyA8qDm57ww==
 
 d3-interpolate@1, d3-interpolate@^1.1.1:
-  version "1.3.2"
-  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.3.2.tgz#417d3ebdeb4bc4efcc8fd4361c55e4040211fd68"
-  integrity sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.3.3.tgz#cef4ba06dfccebcc45e4ae9d4d836a931a945076"
+  integrity sha512-wTsi4AqnC2raZ3Q9eqFxiZGUf5r6YiEdi23vXjjKSWXFYLCQNUtBVMk6uk2tg4cOY6YrjRdmSmI/Mf0ze1zPzQ==
   dependencies:
     d3-color "1"
 
 d3-path@1:
-  version "1.0.8"
-  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.8.tgz#4a0606a794d104513ec4a8af43525f374b278719"
-  integrity sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg==
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
+  integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
 
 d3-queue@^3.0.7:
   version "3.0.7"
@@ -4871,16 +4236,16 @@ d3-scale@^1.0.0:
     d3-time-format "2"
 
 d3-shape@^1.0.0, d3-shape@^1.2.0:
-  version "1.3.5"
-  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.5.tgz#e81aea5940f59f0a79cfccac012232a8987c6033"
-  integrity sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
+  integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
   dependencies:
     d3-path "1"
 
 d3-time-format@2:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.2.1.tgz#971f66aae3b8fb268040494986f41512d4d4dca6"
-  integrity sha512-VA6WqORO1+H1SvSzgl2oT0z3niANh3opa8Cencpen1LFthw/bEX71R/DgjPlWw78J4UHmD0jCPP1W0HpwMkhjg==
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.2.2.tgz#187597ffc6a0f37cb36bb7a1d7167cdc887ecda0"
+  integrity sha512-pweL2Ri2wqMY+wlW/wpkl8T3CUzKAha8S9nmiQlMABab8r5MJN0PD1V4YyRNVaKQfeh4Z0+VO70TLw6ESVOYzw==
   dependencies:
     d3-time "1"
 
@@ -4890,20 +4255,28 @@ d3-time@1:
   integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
 
 d3-timer@^1.0.0:
-  version "1.0.9"
-  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.9.tgz#f7bb8c0d597d792ff7131e1c24a36dd471a471ba"
-  integrity sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5"
+  integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==
 
 d3-voronoi@^1.1.2:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297"
   integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==
 
-d3@~3.5.0, d3@~3.5.17:
+d3@^3.5.17, d3@~3.5.0, d3@~3.5.17:
   version "3.5.17"
   resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
   integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=
 
+d@1, d@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
+  integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
+  dependencies:
+    es5-ext "^0.10.50"
+    type "^1.0.1"
+
 damerau-levenshtein@^1.0.4:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz#780cf7144eb2e8dbd1c3bb83ae31100ccc31a414"
@@ -4965,12 +4338,7 @@ datatables.net@1.10.20, datatables.net@^1.10.15:
   dependencies:
     jquery ">=1.7"
 
-dateformat@^3.0.0:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
-  integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
-
-"debug@0.8.0 - 3.5.0", debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
+"debug@0.8.0 - 3.5.0", debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
   version "3.2.6"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
   integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
@@ -4984,34 +4352,14 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.
   dependencies:
     ms "2.0.0"
 
-debug@3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
-  integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
-  dependencies:
-    ms "2.0.0"
-
-debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
+debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
   integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
   dependencies:
     ms "^2.1.1"
 
-debuglog@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
-  integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
-
-decamelize-keys@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
-  integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=
-  dependencies:
-    decamelize "^1.1.0"
-    map-obj "^1.0.0"
-
-decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.2.0:
+decamelize@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
@@ -5028,11 +4376,6 @@ decode-uri-component@^0.2.0:
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
   integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
 
-dedent@0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
-  integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
-
 deep-equal@^1.0.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
@@ -5063,13 +4406,6 @@ default-gateway@^4.2.0:
     execa "^1.0.0"
     ip-regex "^2.1.0"
 
-defaults@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
-  integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
-  dependencies:
-    clone "^1.0.2"
-
 define-properties@^1.1.2, define-properties@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
@@ -5116,10 +4452,10 @@ delaunator@^4.0.0:
   resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-4.0.1.tgz#3d779687f57919a7a418f8ab947d3bddb6846957"
   integrity sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==
 
-delaunay-find@0.0.3:
-  version "0.0.3"
-  resolved "https://registry.yarnpkg.com/delaunay-find/-/delaunay-find-0.0.3.tgz#b9863465c4cbca963b3d75a54550e73b2fc56c30"
-  integrity sha512-Ex8DtJudrPsB0IhmJxFjHqzZnzbCOoFgw8kTGAnTlc6uU/v25nd7o2HeWhyZSaPhholsfL33PmLSEdaBi0qfug==
+delaunay-find@0.0.5:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/delaunay-find/-/delaunay-find-0.0.5.tgz#5fb37e6509da934881b4b16c08898ac89862c097"
+  integrity sha512-7yAJ/wmKWj3SgqjtkGqT/RCwI0HWAo5YnHMoF5nYXD8cdci+YSo23iPmgrZUNOpDxRWN91PqxUvMMr2lKpjr+w==
   dependencies:
     delaunator "^4.0.0"
 
@@ -5138,11 +4474,6 @@ depd@~1.1.2:
   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
   integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
 
-deprecation@^2.0.0:
-  version "2.3.1"
-  resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
-  integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==
-
 des.js@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
@@ -5156,16 +4487,6 @@ destroy@~1.0.4:
   resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
   integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
 
-detect-file@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
-  integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
-
-detect-indent@6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd"
-  integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==
-
 detect-indent@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
@@ -5173,11 +4494,6 @@ detect-indent@^4.0.0:
   dependencies:
     repeating "^2.0.0"
 
-detect-indent@~5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
-  integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50=
-
 detect-libc@^1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
@@ -5201,14 +4517,6 @@ detect-port-alt@1.1.6:
     address "^1.0.1"
     debug "^2.6.0"
 
-dezalgo@^1.0.0, dezalgo@~1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
-  integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=
-  dependencies:
-    asap "^2.0.0"
-    wrappy "1"
-
 diff-sequences@^24.9.0:
   version "24.9.0"
   resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
@@ -5231,13 +4539,6 @@ dir-glob@2.0.0:
     arrify "^1.0.1"
     path-type "^3.0.0"
 
-dir-glob@^3.0.0, dir-glob@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
-  integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
-  dependencies:
-    path-type "^4.0.0"
-
 dns-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@@ -5287,13 +4588,6 @@ dom-converter@^0.2:
   dependencies:
     utila "~0.4"
 
-dom-helpers@^3.2.0, dom-helpers@^3.2.1, dom-helpers@^3.4.0:
-  version "3.4.0"
-  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
-  integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
-  dependencies:
-    "@babel/runtime" "^7.1.2"
-
 dom-serializer@0:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -5347,35 +4641,23 @@ domutils@^1.5.1, domutils@^1.7.0:
     dom-serializer "0"
     domelementtype "1"
 
-dot-prop@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
-  integrity sha1-G3CK8JSknJoOfbyteQq6U52sEXc=
-  dependencies:
-    is-obj "^1.0.0"
-
-dot-prop@^4.1.0, dot-prop@^4.1.1:
+dot-prop@^4.1.1:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
   integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
   dependencies:
     is-obj "^1.0.0"
 
-dotenv-expand@4.2.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-4.2.0.tgz#def1f1ca5d6059d24a766e587942c21106ce1275"
-  integrity sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU=
+dotenv-expand@5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
 
 dotenv@6.2.0:
   version "6.2.0"
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
   integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
 
-dotenv@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef"
-  integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==
-
 drmonty-datatables-colvis@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/drmonty-datatables-colvis/-/drmonty-datatables-colvis-1.1.2.tgz#96ab9edfb48643cc2edda3f87b88933cdee8127c"
@@ -5383,18 +4665,13 @@ drmonty-datatables-colvis@~1.1.2:
   dependencies:
     jquery ">=1.7.0"
 
-duplexer2@^0.1.2, duplexer2@~0.1.0:
+duplexer2@^0.1.2:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
   integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=
   dependencies:
     readable-stream "^2.0.2"
 
-duplexer3@^0.1.4:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
-  integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
-
 duplexer@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@@ -5418,11 +4695,6 @@ ecc-jsbn@~0.1.1:
     jsbn "~0.1.0"
     safer-buffer "^2.1.0"
 
-editor@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/editor/-/editor-1.0.0.tgz#60c7f87bd62bcc6a894fa8ccd6afb7823a24f742"
-  integrity sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=
-
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -5451,6 +4723,11 @@ emoji-regex@^7.0.1, emoji-regex@^7.0.2:
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
   integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
 
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
 emojis-list@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
@@ -5476,13 +4753,6 @@ encodeurl@~1.0.2:
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
   integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
 
-encoding@^0.1.11:
-  version "0.1.12"
-  resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
-  integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
-  dependencies:
-    iconv-lite "~0.4.13"
-
 end-of-stream@^1.0.0, end-of-stream@^1.1.0:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -5509,19 +4779,6 @@ entities@^2.0.0:
   resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
   integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
 
-env-ci@^4.0.0:
-  version "4.5.1"
-  resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-4.5.1.tgz#2ef014dcb974728b46d1244e491e9e6ccc1923ef"
-  integrity sha512-Xtmr+ordf8POu3NcNzx3eOa2zHyfD4h3fPHX5fLklkWa86ck35n1c9oZmyUnVPUl9zHnpZWdWtCUBPSWEagjCQ==
-  dependencies:
-    execa "^3.2.0"
-    java-properties "^1.0.0"
-
-env-paths@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
-  integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=
-
 eonasdan-bootstrap-datetimepicker@^4.17.47:
   version "4.17.47"
   resolved "https://registry.yarnpkg.com/eonasdan-bootstrap-datetimepicker/-/eonasdan-bootstrap-datetimepicker-4.17.47.tgz#7a49970044065276e7965efd16f822735219e735"
@@ -5532,11 +4789,6 @@ eonasdan-bootstrap-datetimepicker@^4.17.47:
     moment "^2.10"
     moment-timezone "^0.4.0"
 
-err-code@^1.0.0:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
-  integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=
-
 errno@^0.1.3, errno@~0.1.7:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
@@ -5576,17 +4828,31 @@ es-to-primitive@^1.2.0:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
-es6-promise@^4.0.3:
-  version "4.2.8"
-  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
-  integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
+es5-ext@^0.10.35, es5-ext@^0.10.50:
+  version "0.10.52"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.52.tgz#bb21777e919a04263736ded120a9d665f10ea63f"
+  integrity sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==
+  dependencies:
+    es6-iterator "~2.0.3"
+    es6-symbol "~3.1.2"
+    next-tick "~1.0.0"
 
-es6-promisify@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
-  integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
+es6-iterator@2.0.3, es6-iterator@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
+  integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
   dependencies:
-    es6-promise "^4.0.3"
+    d "1"
+    es5-ext "^0.10.35"
+    es6-symbol "^3.1.1"
+
+es6-symbol@^3.1.1, es6-symbol@~3.1.2:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
+  integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
+  dependencies:
+    d "^1.0.1"
+    ext "^1.1.2"
 
 escape-html@~1.0.3:
   version "1.0.3"
@@ -5631,12 +4897,12 @@ eslint-config-prettier@^2.9.0:
   dependencies:
     get-stdin "^5.0.1"
 
-eslint-config-react-app@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-4.0.1.tgz#23fd0fd7ea89442ef1e733f66a7207674b23c8db"
-  integrity sha512-ZsaoXUIGsK8FCi/x4lT2bZR5mMkL/Kgj+Lnw690rbvvUr/uiwgFiD8FcfAhkCycm7Xte6O5lYz4EqMx2vX7jgw==
+eslint-config-react-app@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.0.2.tgz#df40d73a1402986030680c040bbee520db5a32a4"
+  integrity sha512-VhlESAQM83uULJ9jsvcKxx2Ab0yrmjUt8kDz5DyhTQufqWE0ssAnejlWri5LXv25xoXfdqOyeDPdfJS9dXKagQ==
   dependencies:
-    confusing-browser-globals "^1.0.7"
+    confusing-browser-globals "^1.0.9"
 
 eslint-config-standard-jsx@^5.0.0:
   version "5.0.0"
@@ -5663,18 +4929,18 @@ eslint-import-resolver-node@^0.3.2:
     debug "^2.6.9"
     resolve "^1.5.0"
 
-eslint-loader@2.1.2:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.1.2.tgz#453542a1230d6ffac90e4e7cb9cadba9d851be68"
-  integrity sha512-rA9XiXEOilLYPOIInvVH5S/hYfyTPyxag6DZhoQOduM+3TkghAEQ3VcFO8VnX4J4qg/UIBzp72aOf/xvYmpmsg==
+eslint-loader@3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-3.0.2.tgz#5a627316a51d6f41d357b9f6f0554e91506cdd6e"
+  integrity sha512-S5VnD+UpVY1PyYRqeBd/4pgsmkvSokbHqTXAQMpvCyRr3XN2tvSLo9spm2nEpqQqh9dezw3os/0zWihLeOg2Rw==
   dependencies:
-    loader-fs-cache "^1.0.0"
-    loader-utils "^1.0.2"
-    object-assign "^4.0.1"
-    object-hash "^1.1.4"
-    rimraf "^2.6.1"
+    fs-extra "^8.1.0"
+    loader-fs-cache "^1.0.2"
+    loader-utils "^1.2.3"
+    object-hash "^1.3.1"
+    schema-utils "^2.2.0"
 
-eslint-module-utils@^2.3.0, eslint-module-utils@^2.4.0:
+eslint-module-utils@^2.4.0:
   version "2.4.1"
   resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz#7b4675875bf96b0dbf1b21977456e5bb1f5e018c"
   integrity sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==
@@ -5682,30 +4948,14 @@ eslint-module-utils@^2.3.0, eslint-module-utils@^2.4.0:
     debug "^2.6.8"
     pkg-dir "^2.0.0"
 
-eslint-plugin-flowtype@2.50.1:
-  version "2.50.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.50.1.tgz#36d4c961ac8b9e9e1dc091d3fba0537dad34ae8a"
-  integrity sha512-9kRxF9hfM/O6WGZcZPszOVPd2W0TLHBtceulLTsGfwMPtiCCLnCW0ssRiOOiXyqrCA20pm1iXdXm7gQeN306zQ==
-  dependencies:
-    lodash "^4.17.10"
-
-eslint-plugin-import@2.16.0:
-  version "2.16.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz#97ac3e75d0791c4fac0e15ef388510217be7f66f"
-  integrity sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A==
+eslint-plugin-flowtype@3.13.0:
+  version "3.13.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.13.0.tgz#e241ebd39c0ce519345a3f074ec1ebde4cf80f2c"
+  integrity sha512-bhewp36P+t7cEV0b6OdmoRWJCBYRiHFlqPZAG1oS3SF+Y0LQkeDvFSM4oxoxvczD1OdONCXMlJfQFiWLcV9urw==
   dependencies:
-    contains-path "^0.1.0"
-    debug "^2.6.9"
-    doctrine "1.5.0"
-    eslint-import-resolver-node "^0.3.2"
-    eslint-module-utils "^2.3.0"
-    has "^1.0.3"
-    lodash "^4.17.11"
-    minimatch "^3.0.4"
-    read-pkg-up "^2.0.0"
-    resolve "^1.9.0"
+    lodash "^4.17.15"
 
-eslint-plugin-import@^2.13.0:
+eslint-plugin-import@2.18.2, eslint-plugin-import@^2.13.0:
   version "2.18.2"
   resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6"
   integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==
@@ -5727,21 +4977,7 @@ eslint-plugin-jest@^21.15.0:
   resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-21.27.2.tgz#2a795b7c3b5e707df48a953d651042bd01d7b0a8"
   integrity sha512-0E4OIgBJVlAmf1KfYFtZ3gYxgUzC5Eb3Jzmrc9ikI1OY+/cM8Kh72Ti7KfpeHNeD3HJNf9SmEfmvQLIz44Hrhw==
 
-eslint-plugin-jsx-a11y@6.2.1:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz#4ebba9f339b600ff415ae4166e3e2e008831cf0c"
-  integrity sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w==
-  dependencies:
-    aria-query "^3.0.0"
-    array-includes "^3.0.3"
-    ast-types-flow "^0.0.7"
-    axobject-query "^2.0.2"
-    damerau-levenshtein "^1.0.4"
-    emoji-regex "^7.0.2"
-    has "^1.0.3"
-    jsx-ast-utils "^2.0.1"
-
-eslint-plugin-jsx-a11y@^6.0.3:
+eslint-plugin-jsx-a11y@6.2.3, eslint-plugin-jsx-a11y@^6.0.3:
   version "6.2.3"
   resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa"
   integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==
@@ -5800,23 +5036,25 @@ eslint-plugin-promise@^3.7.0:
   resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621"
   integrity sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==
 
-eslint-plugin-react-hooks@^1.5.0:
+eslint-plugin-react-hooks@^1.6.1:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04"
   integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==
 
-eslint-plugin-react@7.12.4:
-  version "7.12.4"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz#b1ecf26479d61aee650da612e425c53a99f48c8c"
-  integrity sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==
+eslint-plugin-react@7.14.3:
+  version "7.14.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13"
+  integrity sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==
   dependencies:
     array-includes "^3.0.3"
     doctrine "^2.1.0"
     has "^1.0.3"
-    jsx-ast-utils "^2.0.1"
+    jsx-ast-utils "^2.1.0"
+    object.entries "^1.1.0"
     object.fromentries "^2.0.0"
-    prop-types "^15.6.2"
-    resolve "^1.9.0"
+    object.values "^1.1.0"
+    prop-types "^15.7.2"
+    resolve "^1.10.1"
 
 eslint-plugin-react@^7.7.0:
   version "7.16.0"
@@ -5856,7 +5094,7 @@ eslint-scope@3.7.1:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
 
-eslint-scope@^4.0.0, eslint-scope@^4.0.3:
+eslint-scope@^4.0.3:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
   integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
@@ -5864,7 +5102,15 @@ eslint-scope@^4.0.0, eslint-scope@^4.0.3:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
 
-eslint-utils@^1.3.1:
+eslint-scope@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
+  integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-utils@^1.4.2, eslint-utils@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
   integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==
@@ -5876,63 +5122,64 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
   integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
 
-eslint@^5.16.0:
-  version "5.16.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea"
-  integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==
+eslint@^6.1.0:
+  version "6.6.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.6.0.tgz#4a01a2fb48d32aacef5530ee9c5a78f11a8afd04"
+  integrity sha512-PpEBq7b6qY/qrOmpYQ/jTMDYfuQMELR4g4WI1M/NaSDDD/bdcMb+dj4Hgks7p41kW2caXsPsEZAEAyAgjVVC0g==
   dependencies:
     "@babel/code-frame" "^7.0.0"
-    ajv "^6.9.1"
+    ajv "^6.10.0"
     chalk "^2.1.0"
     cross-spawn "^6.0.5"
     debug "^4.0.1"
     doctrine "^3.0.0"
-    eslint-scope "^4.0.3"
-    eslint-utils "^1.3.1"
-    eslint-visitor-keys "^1.0.0"
-    espree "^5.0.1"
+    eslint-scope "^5.0.0"
+    eslint-utils "^1.4.3"
+    eslint-visitor-keys "^1.1.0"
+    espree "^6.1.2"
     esquery "^1.0.1"
     esutils "^2.0.2"
     file-entry-cache "^5.0.1"
     functional-red-black-tree "^1.0.1"
-    glob "^7.1.2"
+    glob-parent "^5.0.0"
     globals "^11.7.0"
     ignore "^4.0.6"
     import-fresh "^3.0.0"
     imurmurhash "^0.1.4"
-    inquirer "^6.2.2"
-    js-yaml "^3.13.0"
+    inquirer "^7.0.0"
+    is-glob "^4.0.0"
+    js-yaml "^3.13.1"
     json-stable-stringify-without-jsonify "^1.0.1"
     levn "^0.3.0"
-    lodash "^4.17.11"
+    lodash "^4.17.14"
     minimatch "^3.0.4"
     mkdirp "^0.5.1"
     natural-compare "^1.4.0"
     optionator "^0.8.2"
-    path-is-inside "^1.0.2"
     progress "^2.0.0"
     regexpp "^2.0.1"
-    semver "^5.5.1"
-    strip-ansi "^4.0.0"
-    strip-json-comments "^2.0.1"
+    semver "^6.1.2"
+    strip-ansi "^5.2.0"
+    strip-json-comments "^3.0.1"
     table "^5.2.3"
     text-table "^0.2.0"
+    v8-compile-cache "^2.0.3"
 
-espree@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a"
-  integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==
+espree@^6.1.2:
+  version "6.1.2"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d"
+  integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==
   dependencies:
-    acorn "^6.0.7"
-    acorn-jsx "^5.0.0"
-    eslint-visitor-keys "^1.0.0"
+    acorn "^7.1.0"
+    acorn-jsx "^5.1.0"
+    eslint-visitor-keys "^1.1.0"
 
 esprima@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
   integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
 
-esprima@^4.0.0, esprima@~4.0.0:
+esprima@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
   integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
@@ -5996,19 +5243,6 @@ exec-sh@^0.3.2:
   resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5"
   integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==
 
-execa@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
-  integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
-  dependencies:
-    cross-spawn "^5.0.1"
-    get-stream "^3.0.0"
-    is-stream "^1.1.0"
-    npm-run-path "^2.0.0"
-    p-finally "^1.0.0"
-    signal-exit "^3.0.0"
-    strip-eof "^1.0.0"
-
 execa@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
@@ -6022,22 +5256,6 @@ execa@^1.0.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
-execa@^3.2.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-3.3.0.tgz#7e348eef129a1937f21ecbbd53390942653522c1"
-  integrity sha512-j5Vit5WZR/cbHlqU97+qcnw9WHRCIL4V1SVe75VcHcD1JRBdt8fv0zw89b7CQHQdUHTt2VjuhcF5ibAgVOxqpg==
-  dependencies:
-    cross-spawn "^7.0.0"
-    get-stream "^5.0.0"
-    human-signals "^1.1.1"
-    is-stream "^2.0.0"
-    merge-stream "^2.0.0"
-    npm-run-path "^4.0.0"
-    onetime "^5.1.0"
-    p-finally "^2.0.0"
-    signal-exit "^3.0.2"
-    strip-final-newline "^2.0.0"
-
 exenv@^1.2.2:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
@@ -6061,13 +5279,6 @@ expand-brackets@^2.1.4:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
-expand-tilde@^2.0.0, expand-tilde@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
-  integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=
-  dependencies:
-    homedir-polyfill "^1.0.1"
-
 expect@^24.9.0:
   version "24.9.0"
   resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca"
@@ -6116,6 +5327,13 @@ express@^4.16.2, express@^4.17.1:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
+ext@^1.1.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/ext/-/ext-1.2.0.tgz#8dd8d2dd21bcced3045be09621fa0cbf73908ba4"
+  integrity sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==
+  dependencies:
+    type "^2.0.0"
+
 extend-shallow@^1.1.2:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071"
@@ -6208,17 +5426,6 @@ fast-glob@^2.0.2:
     merge2 "^1.2.3"
     micromatch "^3.1.10"
 
-fast-glob@^3.0.3:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.1.0.tgz#77375a7e3e6f6fc9b18f061cddd28b8d1eec75ae"
-  integrity sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==
-  dependencies:
-    "@nodelib/fs.stat" "^2.0.2"
-    "@nodelib/fs.walk" "^1.2.3"
-    glob-parent "^5.1.0"
-    merge2 "^1.3.0"
-    micromatch "^4.0.2"
-
 fast-json-stable-stringify@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
@@ -6229,13 +5436,6 @@ fast-levenshtein@~2.0.6:
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
   integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
 
-fastq@^1.6.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2"
-  integrity sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==
-  dependencies:
-    reusify "^1.0.0"
-
 faye-websocket@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@@ -6273,20 +5473,7 @@ fbjs-scripts@^0.8.3:
     semver "^5.1.0"
     through2 "^2.0.0"
 
-fbjs@^0.8.1:
-  version "0.8.17"
-  resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
-  integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
-  dependencies:
-    core-js "^1.0.0"
-    isomorphic-fetch "^2.1.1"
-    loose-envify "^1.0.0"
-    object-assign "^4.1.0"
-    promise "^7.1.1"
-    setimmediate "^1.0.5"
-    ua-parser-js "^0.7.18"
-
-figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
+figgy-pudding@^3.5.1:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
   integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
@@ -6335,13 +5522,6 @@ fill-range@^4.0.0:
     repeat-string "^1.6.1"
     to-regex-range "^2.1.0"
 
-fill-range@^7.0.1:
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
-  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
-  dependencies:
-    to-regex-range "^5.0.1"
-
 finalhandler@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@@ -6373,20 +5553,7 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
     make-dir "^2.0.0"
     pkg-dir "^3.0.0"
 
-find-node-modules@2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-2.0.0.tgz#5db1fb9e668a3d451db3d618cd167cdd59e41b69"
-  integrity sha512-8MWIBRgJi/WpjjfVXumjPKCtmQ10B+fjx6zmSA+770GMJirLhWIzg8l763rhjl9xaeaHbnxPNRQKq2mgMhr+aw==
-  dependencies:
-    findup-sync "^3.0.0"
-    merge "^1.2.1"
-
-find-npm-prefix@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf"
-  integrity sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==
-
-find-root@1.1.0, find-root@^1.1.0:
+find-root@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
   integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
@@ -6413,32 +5580,6 @@ find-up@^2.0.0, find-up@^2.1.0:
   dependencies:
     locate-path "^2.0.0"
 
-find-up@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
-  integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
-  dependencies:
-    locate-path "^5.0.0"
-    path-exists "^4.0.0"
-
-find-versions@^3.0.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.1.0.tgz#10161f29cf3eb4350dec10a29bdde75bff0df32d"
-  integrity sha512-NCTfNiVzeE/xL+roNDffGuRbrWI6atI18lTJ22vKp7rs2OhYzMK3W1dIdO2TUndH/QMcacM4d1uWwgcZcHK69Q==
-  dependencies:
-    array-uniq "^2.1.0"
-    semver-regex "^2.0.0"
-
-findup-sync@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1"
-  integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==
-  dependencies:
-    detect-file "^1.0.0"
-    is-glob "^4.0.0"
-    micromatch "^3.0.4"
-    resolve-dir "^1.0.1"
-
 flat-cache@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
@@ -6515,13 +5656,6 @@ for-own@^0.1.3:
... 5404 lines suppressed ...


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