From d916b9e0829283e7f2f5ffd36bd7500c8f4ba741 Mon Sep 17 00:00:00 2001
From: gpetrochenkov-usgs <gpetrochenkov@usgs.gov>
Date: Fri, 27 Mar 2020 17:10:22 -0400
Subject: [PATCH] Review Revisions

---
 assets/Makefile                               |   2 +-
 assets/karma.conf.js                          |   1 +
 assets/package.json                           |   5 +-
 assets/rollup.config.js                       |  10 +-
 assets/src/scripts/ajax.spec.js               |   2 +-
 .../components/dailyValueHydrograph/index.js  |   2 +-
 .../dailyValueHydrograph/index.spec.js        |   2 +-
 .../time-series-graph.spec.js                 |   2 +-
 .../dailyValueHydrograph/tooltip.js           |   2 +-
 .../dailyValueHydrograph/tooltip.spec.js      |   2 +-
 assets/src/scripts/components/embed.js        |   2 +-
 assets/src/scripts/components/embed.spec.js   |   4 +-
 .../scripts/components/hydrograph/audible.js  |   2 +-
 .../components/hydrograph/audible.spec.js     |   6 +-
 .../components/hydrograph/cursor.spec.js      |   2 +-
 .../components/hydrograph/date-controls.js    |   2 +-
 .../hydrograph/date-controls.spec.js          |   4 +-
 .../scripts/components/hydrograph/domain.js   |  12 +-
 .../hydrograph/drawing-data.spec.js           |   4 +-
 .../components/hydrograph/graph-brush.js      |   4 +-
 .../components/hydrograph/graph-brush.spec.js |   2 +-
 .../components/hydrograph/graph-controls.js   |   2 +-
 .../hydrograph/graph-controls.spec.js         |   6 +-
 .../scripts/components/hydrograph/index.js    |   2 +-
 .../components/hydrograph/index.spec.js       |   6 +-
 .../components/hydrograph/layout.spec.js      |   4 +-
 .../components/hydrograph/legend.spec.js      |   2 +-
 .../components/hydrograph/method-picker.js    |   2 +-
 .../hydrograph/method-picker.spec.js          |   6 +-
 .../components/hydrograph/parameters.js       |   2 +-
 .../components/hydrograph/parameters.spec.js  |   8 +-
 .../components/hydrograph/scales.spec.js      |   6 +-
 .../components/hydrograph/time-series-data.js |   4 +-
 .../hydrograph/time-series-graph.spec.js      |   4 +-
 .../scripts/components/hydrograph/tooltip.js  |   2 +-
 .../components/hydrograph/tooltip.spec.js     |   4 +-
 .../scripts/components/map/flood-slider.js    |  10 +-
 .../components/map/flood-slider.spec.js       |   6 +-
 assets/src/scripts/components/map/index.js    |  24 +-
 .../src/scripts/components/map/index.spec.js  |   6 +-
 .../d3-rendering/accessibility.spec.js        |   4 +-
 .../src/scripts/d3-rendering/legend.spec.js   |   2 +-
 .../d3-rendering/loading-indicator.spec.js    |   2 +-
 .../src/scripts/d3-rendering/markers.spec.js  |   4 +-
 assets/src/scripts/helpers.spec.js            |   4 +-
 assets/src/scripts/index.js                   |   2 +-
 assets/src/scripts/index.spec.js              |   2 +-
 assets/src/scripts/networks/index.js          |  12 +-
 assets/src/scripts/networks/index.spec.js     |   5 +
 .../index.js                                  |  42 +-
 .../networks/network-component/index.spec.js  | 136 +++++
 .../network-elements.js}                      |  49 +-
 .../network-legend.js                         |  30 +-
 .../network-component/network-legend.spec.js  |  87 +++
 assets/src/scripts/networks/network-store.js  |  53 --
 .../{ => selectors}/network-data-selector.js  |   2 +-
 .../network-data-selector.spec.js             |  10 +-
 .../{ => store}/network-data-reducer.js       |   0
 .../{ => store}/network-data-reducer.spec.js  |   6 +-
 .../scripts/networks/store/network-store.js   |  57 ++
 .../{ => web-services}/network-data.js        |   6 +-
 .../{ => web-services}/network-data.spec.js   |   5 +-
 assets/src/scripts/schema.js                  |   4 +-
 assets/src/scripts/schema.spec.js             |   2 +-
 .../scripts/store/flood-data-reducer.spec.js  |   2 +-
 .../scripts/store/flood-state-reducer.spec.js |   2 +-
 assets/src/scripts/store/index.js             | 578 +++++++++++++++++-
 .../{site-store.spec.js => index.spec.js}     |   4 +-
 .../scripts/store/nldi-data-reducer.spec.js   |   2 +-
 .../src/scripts/store/series-reducer.spec.js  |   2 +-
 assets/src/scripts/store/site-store.js        | 560 -----------------
 .../store/time-series-state-reducer.spec.js   |   2 +-
 assets/src/scripts/store/ui-reducer.spec.js   |   2 +-
 assets/src/scripts/tooltips.spec.js           |   4 +-
 assets/src/scripts/url-params.spec.js         |   3 +-
 assets/src/scripts/web-services/flood-data.js |   2 +-
 .../scripts/web-services/flood-data.spec.js   |   2 +-
 assets/src/scripts/web-services/nldi-data.js  |   2 +-
 .../scripts/web-services/nldi-data.spec.js    |   2 +-
 .../src/scripts/web-services/observations.js  |   2 +-
 .../scripts/web-services/statistics-data.js   |   2 +-
 .../web-services/statistics-data.spec.js      |   2 +-
 .../{_network-map.scss => _network.scss}      |   0
 assets/src/styles/less/vendor.less            |   4 -
 assets/src/styles/network.scss                | 133 +++-
 assets/tests/scripts/networks/karma.conf.js   | 360 +++++++++++
 graph-server/Dockerfile                       |   2 +-
 graph-server/README.md                        |   4 +-
 wdfn-server/Makefile                          |   2 +-
 wdfn-server/waterdata/__init__.py             |   3 +-
 wdfn-server/waterdata/services/ogc.py         |   3 -
 .../waterdata/templates/base_network.html     |  13 +-
 .../templates/macros/components.html          |  32 +-
 wdfn-server/waterdata/templates/networks.html |  11 +-
 wdfn-server/waterdata/utils.py                |   1 +
 95 files changed, 1541 insertions(+), 908 deletions(-)
 create mode 100644 assets/src/scripts/networks/index.spec.js
 rename assets/src/scripts/networks/{components => network-component}/index.js (61%)
 create mode 100644 assets/src/scripts/networks/network-component/index.spec.js
 rename assets/src/scripts/networks/{components/network-mapping.js => network-component/network-elements.js} (63%)
 rename assets/src/scripts/networks/{components => network-component}/network-legend.js (72%)
 create mode 100644 assets/src/scripts/networks/network-component/network-legend.spec.js
 delete mode 100644 assets/src/scripts/networks/network-store.js
 rename assets/src/scripts/networks/{ => selectors}/network-data-selector.js (86%)
 rename assets/src/scripts/networks/{ => selectors}/network-data-selector.spec.js (73%)
 rename assets/src/scripts/networks/{ => store}/network-data-reducer.js (100%)
 rename assets/src/scripts/networks/{ => store}/network-data-reducer.spec.js (97%)
 create mode 100644 assets/src/scripts/networks/store/network-store.js
 rename assets/src/scripts/networks/{ => web-services}/network-data.js (89%)
 rename assets/src/scripts/networks/{ => web-services}/network-data.spec.js (99%)
 rename assets/src/scripts/store/{site-store.spec.js => index.spec.js} (99%)
 delete mode 100644 assets/src/scripts/store/site-store.js
 rename assets/src/styles/components/{_network-map.scss => _network.scss} (100%)
 delete mode 100644 assets/src/styles/less/vendor.less
 create mode 100644 assets/tests/scripts/networks/karma.conf.js

diff --git a/assets/Makefile b/assets/Makefile
index 01045bfc6..5a803ee06 100644
--- a/assets/Makefile
+++ b/assets/Makefile
@@ -16,7 +16,7 @@ clean-assets:
 #
 test-assets:
 	@echo 'Running assets tests...'
-	cd assets && npm run test
+	cd assets && npm run test ./karma.conf.js && npm run test ./tests/scripts/networks/karma.conf.js
 
 watch-assets:
 	cd assets && npm run watch
diff --git a/assets/karma.conf.js b/assets/karma.conf.js
index 8d9b026c9..f381c6e04 100644
--- a/assets/karma.conf.js
+++ b/assets/karma.conf.js
@@ -98,6 +98,7 @@ module.exports = function (config) {
                         exclude: [
                             'tests/**/*.js',
                             'src/scripts/**/*.spec.js',
+                            'src/scripts/networks/*.js',
                             'node_modules/**/*'
                         ]
                     })
diff --git a/assets/package.json b/assets/package.json
index f4d35b5d9..37a37a7f1 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -14,7 +14,6 @@
     "build:css:main": "sass --load-path node_modules/leaflet/dist --load-path node_modules/wdfn-viz/src/stylesheets --load-path node_modules/uswds/src/stylesheets src/styles/main.scss dist/main.css && postcss dist/main.css --map -o dist/main.css",
     "build:css:graph": "sass --load-path node_modules/wdfn-viz/src/stylesheets --load-path node_modules/uswds/src/stylesheets src/styles/graph.scss dist/graph.css && postcss dist/graph.css --map -o dist/graph.css",
     "build:css:network": "sass --load-path node_modules/leaflet/dist --load-path node_modules/wdfn-viz/src/stylesheets --load-path node_modules/uswds/src/stylesheets src/styles/network.scss dist/network.css && postcss dist/network.css --map -o dist/network.css",
-    "build:css:vendor": "lessc src/styles/less/vendor.less > dist/vendor.css && uglifycss --output dist/vendor.css dist/vendor.css && postcss dist/vendor.css --no-map --use autoprefixer -o dist/vendor.css",
     "build:fonts": "mkdir -p dist/fonts && cp -r node_modules/uswds/src/fonts/* dist/fonts && cp node_modules/@fortawesome/fontawesome-free/webfonts/* dist/fonts",
     "build:images": "mkdir -p dist/img && mkdir -p dist/images && cp -r node_modules/uswds/src/img/* dist/img && cp -r node_modules/wdfn-viz/src/img/* dist/img && cp -r node_modules/leaflet/dist/images/* dist/images && cp -r src/img/* dist/img",
     "build:js": "rollup -c --environment NODE_ENV:production && mkdir -p dist/scripts && cp node_modules/date-time-format-timezone/build/browserified/date-time-format-timezone-complete-min.js dist/scripts",
@@ -63,7 +62,6 @@
     "karma-rollup-preprocessor": "^7.0.3",
     "karma-sauce-launcher": "^2.0.2",
     "karma-spec-reporter": "0.0.32",
-    "less": "3.11.1",
     "live-server": "^1.2.1",
     "nodemon": "^2.0.2",
     "npm-run-all": "^4.1.5",
@@ -92,10 +90,10 @@
     "date-time-format-timezone": "1.0.22",
     "element-closest": "3.0.2",
     "esri-leaflet": "2.3.3",
-    "exports-loader": "0.7.0",
     "fast-memoize": "2.5.2",
     "leaflet": "1.6.0",
     "leaflet.markercluster": "1.4.1",
+    "list.js": "^1.5.0",
     "lodash": "4.17.15",
     "luxon": "1.22.0",
     "matchmedia-polyfill": "0.3.2",
@@ -104,7 +102,6 @@
     "redux": "4.0.5",
     "redux-thunk": "2.3.0",
     "reselect": "4.0.0",
-    "tabulator-tables": "4.5.3",
     "wdfn-viz": "1.4.0"
   }
 }
diff --git a/assets/rollup.config.js b/assets/rollup.config.js
index e15bab3bb..aa061772a 100644
--- a/assets/rollup.config.js
+++ b/assets/rollup.config.js
@@ -17,7 +17,7 @@ const env = process.env.NODE_ENV || 'development';
 
 const getBundleConfig = function (src, dest) {
 
-    return {
+    const configMap = {
         input: src,
         plugins: [
             alias({
@@ -60,6 +60,14 @@ const getBundleConfig = function (src, dest) {
         },
         treeshake: env === 'production'
     };
+
+    if (src == 'src/scripts/networks/index.js'){
+        configMap['external'] = {
+           window: 'window'
+        };
+    }
+
+    return configMap;
 };
 
 module.exports = [
diff --git a/assets/src/scripts/ajax.spec.js b/assets/src/scripts/ajax.spec.js
index 6b05fee0f..ddec77470 100644
--- a/assets/src/scripts/ajax.spec.js
+++ b/assets/src/scripts/ajax.spec.js
@@ -1,4 +1,4 @@
-import { get } from './ajax';
+import {get} from './ajax';
 
 describe('ajax module', () => {
     describe('get', () => {
diff --git a/assets/src/scripts/components/dailyValueHydrograph/index.js b/assets/src/scripts/components/dailyValueHydrograph/index.js
index a4151c61d..522b066c0 100644
--- a/assets/src/scripts/components/dailyValueHydrograph/index.js
+++ b/assets/src/scripts/components/dailyValueHydrograph/index.js
@@ -1,6 +1,6 @@
 import {select} from 'd3-selection';
 
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 
 import {drawErrorAlert, drawInfoAlert} from '../../d3-rendering/alerts';
 import {drawLoadingIndicator} from '../../d3-rendering/loading-indicator';
diff --git a/assets/src/scripts/components/dailyValueHydrograph/index.spec.js b/assets/src/scripts/components/dailyValueHydrograph/index.spec.js
index 8cc790bcd..acb8c9807 100644
--- a/assets/src/scripts/components/dailyValueHydrograph/index.spec.js
+++ b/assets/src/scripts/components/dailyValueHydrograph/index.spec.js
@@ -1,6 +1,6 @@
 import {select} from 'd3-selection';
 
-import {configureStore, Actions} from '../../store/site-store';
+import {configureStore, Actions} from '../../store';
 
 import {attachToNode} from './index';
 
diff --git a/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js b/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js
index 6fb09676d..c45625a51 100644
--- a/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js
+++ b/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js
@@ -1,6 +1,6 @@
 import {select} from 'd3-selection';
 
-import {configureStore, Actions} from '../../store/site-store';
+import {configureStore, Actions} from '../../store';
 
 import {drawTimeSeriesGraph} from './time-series-graph';
 
diff --git a/assets/src/scripts/components/dailyValueHydrograph/tooltip.js b/assets/src/scripts/components/dailyValueHydrograph/tooltip.js
index 433714d55..1e819c732 100644
--- a/assets/src/scripts/components/dailyValueHydrograph/tooltip.js
+++ b/assets/src/scripts/components/dailyValueHydrograph/tooltip.js
@@ -6,7 +6,7 @@ import {drawCursorSlider} from '../../d3-rendering/cursor-slider';
 import {drawFocusCircles, drawFocusOverlay, drawFocusLine} from '../../d3-rendering/graph-tooltip';
 import {link} from '../../lib/d3-redux';
 import {getObservationsCursorOffset, getCurrentObservationsTimeSeriesUnitOfMeasure} from '../../selectors/observations-selector';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 
 import {APPROVED, ESTIMATED} from './time-series-graph';
 import {getLayout} from './selectors/layout';
diff --git a/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js b/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js
index 87bd1de4d..6162fdb55 100644
--- a/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js
+++ b/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js
@@ -1,6 +1,6 @@
 import {select} from 'd3-selection';
 
-import {configureStore} from '../../store/site-store';
+import {configureStore} from '../../store';
 
 import {drawTooltipFocus, drawTooltipText, drawTooltipCursorSlider} from './tooltip';
 
diff --git a/assets/src/scripts/components/embed.js b/assets/src/scripts/components/embed.js
index 5c160b83f..98cb51e96 100644
--- a/assets/src/scripts/components/embed.js
+++ b/assets/src/scripts/components/embed.js
@@ -1,4 +1,4 @@
-import { select } from 'd3-selection';
+import {select} from 'd3-selection';
 
 
 /*
diff --git a/assets/src/scripts/components/embed.spec.js b/assets/src/scripts/components/embed.spec.js
index bdf460b57..4efaab332 100644
--- a/assets/src/scripts/components/embed.spec.js
+++ b/assets/src/scripts/components/embed.spec.js
@@ -1,5 +1,5 @@
-import { select } from 'd3-selection';
-import { attachToNode } from './embed';
+import {select} from 'd3-selection';
+import {attachToNode} from './embed';
 
 
 describe('embed component', () => {
diff --git a/assets/src/scripts/components/hydrograph/audible.js b/assets/src/scripts/components/hydrograph/audible.js
index 3897afb24..7c7985eb1 100644
--- a/assets/src/scripts/components/hydrograph/audible.js
+++ b/assets/src/scripts/components/hydrograph/audible.js
@@ -8,7 +8,7 @@ import { tsCursorPointsSelector } from './cursor';
 import { getMainXScale, getMainYScale } from './scales';
 import config from '../../config';
 import { link } from '../../lib/d3-redux';
-import { Actions } from '../../store/site-store';
+import { Actions } from '../../store';
 
 // Higher tones get lower volume
 const volumeScale = scaleLinear().range([2, .3]);
diff --git a/assets/src/scripts/components/hydrograph/audible.spec.js b/assets/src/scripts/components/hydrograph/audible.spec.js
index fe9ed994c..669fb6c34 100644
--- a/assets/src/scripts/components/hydrograph/audible.spec.js
+++ b/assets/src/scripts/components/hydrograph/audible.spec.js
@@ -1,6 +1,6 @@
-import { select } from 'd3-selection';
-import { audibleUI } from './audible';
-import { configureStore } from '../../store/site-store';
+import {select} from 'd3-selection';
+import {audibleUI} from './audible';
+import {configureStore} from '../../store';
 
 
 const TEST_STATE = {
diff --git a/assets/src/scripts/components/hydrograph/cursor.spec.js b/assets/src/scripts/components/hydrograph/cursor.spec.js
index 9788297e8..4dff5076b 100644
--- a/assets/src/scripts/components/hydrograph/cursor.spec.js
+++ b/assets/src/scripts/components/hydrograph/cursor.spec.js
@@ -1,4 +1,4 @@
-import {Actions, configureStore} from '../../store/site-store';
+import {Actions, configureStore} from '../../store';
 import {tsCursorPointsSelector, cursorOffsetSelector} from './cursor';
 
 let DATA = [12, 13, 14, 15, 16].map(hour => {
diff --git a/assets/src/scripts/components/hydrograph/date-controls.js b/assets/src/scripts/components/hydrograph/date-controls.js
index b15dc6a97..b342342a3 100644
--- a/assets/src/scripts/components/hydrograph/date-controls.js
+++ b/assets/src/scripts/components/hydrograph/date-controls.js
@@ -11,7 +11,7 @@ import {
     getCustomTimeRange,
     getIanaTimeZone
 } from '../../selectors/time-series-selector';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 
 
 export const drawDateRangeControls = function(elem, store, siteno) {
diff --git a/assets/src/scripts/components/hydrograph/date-controls.spec.js b/assets/src/scripts/components/hydrograph/date-controls.spec.js
index e282ba124..e3d5f83c2 100644
--- a/assets/src/scripts/components/hydrograph/date-controls.spec.js
+++ b/assets/src/scripts/components/hydrograph/date-controls.spec.js
@@ -1,6 +1,6 @@
-import { select } from 'd3-selection';
+import {select} from 'd3-selection';
 
-import { Actions, configureStore } from '../../store/site-store';
+import {Actions, configureStore} from '../../store';
 
 import {drawDateRangeControls} from './date-controls';
 
diff --git a/assets/src/scripts/components/hydrograph/domain.js b/assets/src/scripts/components/hydrograph/domain.js
index d82cbdff6..0efe5c58d 100644
--- a/assets/src/scripts/components/hydrograph/domain.js
+++ b/assets/src/scripts/components/hydrograph/domain.js
@@ -1,10 +1,10 @@
-import { extent, ticks } from 'd3-array';
-import { format } from 'd3-format';
-import { createSelector } from 'reselect';
-import { mediaQuery } from '../../utils';
+import {extent, ticks} from 'd3-array';
+import {format} from 'd3-format';
+import {createSelector} from 'reselect';
+import {mediaQuery} from '../../utils';
 import config from '../../config';
-import { visiblePointsSelector } from './drawing-data';
-import { getCurrentParmCd } from '../../selectors/time-series-selector';
+import {visiblePointsSelector} from './drawing-data';
+import {getCurrentParmCd} from '../../selectors/time-series-selector';
 
 
 const PADDING_RATIO = 0.2;
diff --git a/assets/src/scripts/components/hydrograph/drawing-data.spec.js b/assets/src/scripts/components/hydrograph/drawing-data.spec.js
index ed0f6bd28..74a74613e 100644
--- a/assets/src/scripts/components/hydrograph/drawing-data.spec.js
+++ b/assets/src/scripts/components/hydrograph/drawing-data.spec.js
@@ -1,6 +1,6 @@
 
-import { DateTime } from 'luxon';
-import { lineSegmentsSelector, pointsSelector, allPointsSelector, pointsByTsKeySelector, classesForPoint, lineSegmentsByParmCdSelector, currentVariableLineSegmentsSelector, currentVariablePointsSelector, currentVariablePointsByTsIdSelector, visiblePointsSelector, getCurrentVariableMedianStatPoints, MAX_LINE_POINT_GAP } from './drawing-data';
+import {DateTime} from 'luxon';
+import {lineSegmentsSelector, pointsSelector, allPointsSelector, pointsByTsKeySelector, classesForPoint, lineSegmentsByParmCdSelector, currentVariableLineSegmentsSelector, currentVariablePointsSelector, currentVariablePointsByTsIdSelector, visiblePointsSelector, getCurrentVariableMedianStatPoints, MAX_LINE_POINT_GAP} from './drawing-data';
 
 const TEST_DATA = {
     series: {
diff --git a/assets/src/scripts/components/hydrograph/graph-brush.js b/assets/src/scripts/components/hydrograph/graph-brush.js
index 0244dc8aa..059ca117c 100644
--- a/assets/src/scripts/components/hydrograph/graph-brush.js
+++ b/assets/src/scripts/components/hydrograph/graph-brush.js
@@ -1,10 +1,10 @@
 import {brushX, brushSelection} from 'd3-brush';
-import { event } from 'd3-selection';
+import {event} from 'd3-selection';
 import {createStructuredSelector} from 'reselect';
 
 import {appendXAxis} from '../../d3-rendering/axes';
 import {link} from '../../lib/d3-redux';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 
 import {getBrushXAxis} from './axes';
 import {currentVariableLineSegmentsSelector} from './drawing-data';
diff --git a/assets/src/scripts/components/hydrograph/graph-brush.spec.js b/assets/src/scripts/components/hydrograph/graph-brush.spec.js
index c1c5fe6df..eb9769131 100644
--- a/assets/src/scripts/components/hydrograph/graph-brush.spec.js
+++ b/assets/src/scripts/components/hydrograph/graph-brush.spec.js
@@ -1,6 +1,6 @@
 import {select} from 'd3-selection';
 
-import{configureStore} from '../../store/site-store';
+import{configureStore} from '../../store';
 
 import {drawGraphBrush} from './graph-brush';
 
diff --git a/assets/src/scripts/components/hydrograph/graph-controls.js b/assets/src/scripts/components/hydrograph/graph-controls.js
index 33742ef25..6d616eebc 100644
--- a/assets/src/scripts/components/hydrograph/graph-controls.js
+++ b/assets/src/scripts/components/hydrograph/graph-controls.js
@@ -1,7 +1,7 @@
 
 import {link} from '../../lib/d3-redux';
 import {getCurrentVariableTimeSeries} from '../../selectors/time-series-selector';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 
 import {audibleUI} from './audible';
 import {getCurrentVariableMedianStatistics} from '../../selectors/median-statistics-selector';
diff --git a/assets/src/scripts/components/hydrograph/graph-controls.spec.js b/assets/src/scripts/components/hydrograph/graph-controls.spec.js
index f08d3f82c..426b34737 100644
--- a/assets/src/scripts/components/hydrograph/graph-controls.spec.js
+++ b/assets/src/scripts/components/hydrograph/graph-controls.spec.js
@@ -1,6 +1,6 @@
-import {Actions, configureStore} from '../../store/site-store';
-import { select } from 'd3-selection';
-import { drawGraphControls } from './graph-controls';
+import {Actions, configureStore} from '../../store';
+import {select} from 'd3-selection';
+import {drawGraphControls} from './graph-controls';
 
 // Tests for the graph-controls module
 describe('graph-controls', () => {
diff --git a/assets/src/scripts/components/hydrograph/index.js b/assets/src/scripts/components/hydrograph/index.js
index 5c610b3ee..1e21cdd1a 100644
--- a/assets/src/scripts/components/hydrograph/index.js
+++ b/assets/src/scripts/components/hydrograph/index.js
@@ -9,7 +9,7 @@ import {drawWarningAlert, drawInfoAlert} from '../../d3-rendering/alerts';
 
 import {link} from '../../lib/d3-redux';
 import {hasAnyTimeSeries, getCurrentParmCd, getVariables} from '../../selectors/time-series-selector';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 import {renderTimeSeriesUrlParams} from '../../url-params';
 
 import {drawDateRangeControls} from './date-controls';
diff --git a/assets/src/scripts/components/hydrograph/index.spec.js b/assets/src/scripts/components/hydrograph/index.spec.js
index a2203caea..e7b0aa267 100644
--- a/assets/src/scripts/components/hydrograph/index.spec.js
+++ b/assets/src/scripts/components/hydrograph/index.spec.js
@@ -1,6 +1,6 @@
-import { select, selectAll } from 'd3-selection';
-import { attachToNode } from './index';
-import { Actions, configureStore } from '../../store/site-store';
+import {select, selectAll} from 'd3-selection';
+import {attachToNode} from './index';
+import {Actions, configureStore} from '../../store';
 
 
 const TEST_STATE = {
diff --git a/assets/src/scripts/components/hydrograph/layout.spec.js b/assets/src/scripts/components/hydrograph/layout.spec.js
index dfe83ca5e..b8a3a1b75 100644
--- a/assets/src/scripts/components/hydrograph/layout.spec.js
+++ b/assets/src/scripts/components/hydrograph/layout.spec.js
@@ -1,6 +1,6 @@
-import { format } from 'd3-format';
+import {format} from 'd3-format';
 
-import { getMainLayout, ASPECT_RATIO } from './layout';
+import {getMainLayout, ASPECT_RATIO} from './layout';
 
 describe('layout module', () => {
 
diff --git a/assets/src/scripts/components/hydrograph/legend.spec.js b/assets/src/scripts/components/hydrograph/legend.spec.js
index c32d79158..dea6bd059 100644
--- a/assets/src/scripts/components/hydrograph/legend.spec.js
+++ b/assets/src/scripts/components/hydrograph/legend.spec.js
@@ -1,7 +1,7 @@
 import {select, selectAll} from 'd3-selection';
 import {legendMarkerRowsSelector, drawTimeSeriesLegend} from './legend';
 import {lineMarker, rectangleMarker, textOnlyMarker} from '../../d3-rendering/markers';
-import {Actions, configureStore} from '../../store/site-store';
+import {Actions, configureStore} from '../../store';
 
 describe('UV: Legend module', () => {
 
diff --git a/assets/src/scripts/components/hydrograph/method-picker.js b/assets/src/scripts/components/hydrograph/method-picker.js
index 2912d271b..91bff95e9 100644
--- a/assets/src/scripts/components/hydrograph/method-picker.js
+++ b/assets/src/scripts/components/hydrograph/method-picker.js
@@ -8,7 +8,7 @@ import {createStructuredSelector} from 'reselect';
 import{link}  from '../../lib/d3-redux';
 import config from '../../config';
 import {getCurrentMethodID, getAllMethodsForCurrentVariable} from '../../selectors/time-series-selector';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 
 import { } from './time-series';
 
diff --git a/assets/src/scripts/components/hydrograph/method-picker.spec.js b/assets/src/scripts/components/hydrograph/method-picker.spec.js
index ac6cc38ad..ba80d119c 100644
--- a/assets/src/scripts/components/hydrograph/method-picker.spec.js
+++ b/assets/src/scripts/components/hydrograph/method-picker.spec.js
@@ -1,8 +1,8 @@
-import { select } from 'd3-selection';
+import {select} from 'd3-selection';
 
-import { configureStore } from '../../store/site-store';
+import {configureStore} from '../../store';
 
-import { drawMethodPicker } from './method-picker';
+import {drawMethodPicker} from './method-picker';
 
 describe('method-picker', () => {
 
diff --git a/assets/src/scripts/components/hydrograph/parameters.js b/assets/src/scripts/components/hydrograph/parameters.js
index dc86280ba..13ef13f38 100644
--- a/assets/src/scripts/components/hydrograph/parameters.js
+++ b/assets/src/scripts/components/hydrograph/parameters.js
@@ -5,7 +5,7 @@ import {select} from 'd3-selection';
 import {getVariables, getCurrentVariableID, getTimeSeries} from '../../selectors/time-series-selector';
 
 import config from '../../config';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 import {appendTooltip} from '../../tooltips';
 import {sortedParameters} from '../../utils';
 
diff --git a/assets/src/scripts/components/hydrograph/parameters.spec.js b/assets/src/scripts/components/hydrograph/parameters.spec.js
index 8bea383cc..958dde5f9 100644
--- a/assets/src/scripts/components/hydrograph/parameters.spec.js
+++ b/assets/src/scripts/components/hydrograph/parameters.spec.js
@@ -1,7 +1,7 @@
-import { select } from 'd3-selection';
-import { scaleLinear } from 'd3-scale';
-import { addSparkLine, plotSeriesSelectTable, availableTimeSeriesSelector } from './parameters';
-import { configureStore} from '../../store/site-store';
+import {select} from 'd3-selection';
+import {scaleLinear} from 'd3-scale';
+import {addSparkLine, plotSeriesSelectTable, availableTimeSeriesSelector} from './parameters';
+import {configureStore} from '../../store';
 
 
 describe('Parameters module', () => {
diff --git a/assets/src/scripts/components/hydrograph/scales.spec.js b/assets/src/scripts/components/hydrograph/scales.spec.js
index 2eec18d6f..c0e85991b 100644
--- a/assets/src/scripts/components/hydrograph/scales.spec.js
+++ b/assets/src/scripts/components/hydrograph/scales.spec.js
@@ -1,6 +1,6 @@
-import { extent } from 'd3-array';
-import { DateTime } from 'luxon';
-import { createXScale, createYScale, getMainYScale, getBrushYScale, getSecondaryYScale } from './scales';
+import {extent} from 'd3-array';
+import {DateTime} from 'luxon';
+import {createXScale, createYScale, getMainYScale, getBrushYScale, getSecondaryYScale} from './scales';
 
 
 describe('scales', () => {
diff --git a/assets/src/scripts/components/hydrograph/time-series-data.js b/assets/src/scripts/components/hydrograph/time-series-data.js
index 17ad0b39f..1b01b111e 100644
--- a/assets/src/scripts/components/hydrograph/time-series-data.js
+++ b/assets/src/scripts/components/hydrograph/time-series-data.js
@@ -1,5 +1,5 @@
-import { extent } from 'd3-array';
-import { line as d3Line } from 'd3-shape';
+import {extent} from 'd3-array';
+import {line as d3Line} from 'd3-shape';
 
 import {HASH_ID, MASK_DESC} from './drawing-data';
 
diff --git a/assets/src/scripts/components/hydrograph/time-series-graph.spec.js b/assets/src/scripts/components/hydrograph/time-series-graph.spec.js
index 3044ffb76..f6d6b998a 100644
--- a/assets/src/scripts/components/hydrograph/time-series-graph.spec.js
+++ b/assets/src/scripts/components/hydrograph/time-series-graph.spec.js
@@ -1,6 +1,6 @@
-import { select, selectAll } from 'd3-selection';
+import {select, selectAll} from 'd3-selection';
 
-import {Actions, configureStore} from '../../store/site-store';
+import {Actions, configureStore} from '../../store';
 
 import {drawTimeSeriesGraph} from './time-series-graph';
 
diff --git a/assets/src/scripts/components/hydrograph/tooltip.js b/assets/src/scripts/components/hydrograph/tooltip.js
index fa56d888b..fd9bd4cdd 100644
--- a/assets/src/scripts/components/hydrograph/tooltip.js
+++ b/assets/src/scripts/components/hydrograph/tooltip.js
@@ -10,7 +10,7 @@ import {drawCursorSlider} from '../../d3-rendering/cursor-slider';
 import {drawFocusOverlay, drawFocusCircles, drawFocusLine} from '../../d3-rendering/graph-tooltip';
 import {link} from '../../lib/d3-redux';
 import {getCurrentVariable, getCurrentParmCd} from '../../selectors/time-series-selector';
-import {Actions} from '../../store/site-store';
+import {Actions} from '../../store';
 import {mediaQuery, convertCelsiusToFahrenheit, convertFahrenheitToCelsius} from '../../utils';
 
 import {cursorTimeSelector, tsCursorPointsSelector} from './cursor';
diff --git a/assets/src/scripts/components/hydrograph/tooltip.spec.js b/assets/src/scripts/components/hydrograph/tooltip.spec.js
index 0aced6cbe..ab6670065 100644
--- a/assets/src/scripts/components/hydrograph/tooltip.spec.js
+++ b/assets/src/scripts/components/hydrograph/tooltip.spec.js
@@ -1,5 +1,5 @@
-import { select } from 'd3-selection';
-import { Actions, configureStore } from '../../store/site-store';
+import {select} from 'd3-selection';
+import {Actions, configureStore} from '../../store';
 import {drawTooltipText, drawTooltipFocus, tooltipPointsSelector, drawTooltipCursorSlider} from './tooltip';
 
 
diff --git a/assets/src/scripts/components/map/flood-slider.js b/assets/src/scripts/components/map/flood-slider.js
index e4593af34..eebb2775a 100644
--- a/assets/src/scripts/components/map/flood-slider.js
+++ b/assets/src/scripts/components/map/flood-slider.js
@@ -1,8 +1,8 @@
-import { createStructuredSelector } from 'reselect';
-import { link } from '../../lib/d3-redux';
-import { getFloodStages, getFloodStageHeight, getFloodGageHeightStageIndex, hasFloodData } from '../../selectors/flood-data-selector';
-import { Actions } from '../../store/site-store';
-import { appendTooltip } from '../../tooltips';
+import {createStructuredSelector} from 'reselect';
+import {link} from '../../lib/d3-redux';
+import {getFloodStages, getFloodStageHeight, getFloodGageHeightStageIndex, hasFloodData} from '../../selectors/flood-data-selector';
+import {Actions} from '../../store';
+import {appendTooltip} from '../../tooltips';
 
 
 
diff --git a/assets/src/scripts/components/map/flood-slider.spec.js b/assets/src/scripts/components/map/flood-slider.spec.js
index 1e0ee66a3..abaade4f9 100644
--- a/assets/src/scripts/components/map/flood-slider.spec.js
+++ b/assets/src/scripts/components/map/flood-slider.spec.js
@@ -1,6 +1,6 @@
-import { select } from 'd3-selection';
-import { configureStore } from '../../store/site-store';
-import { floodSlider } from './flood-slider';
+import {select} from 'd3-selection';
+import {configureStore} from '../../store';
+import {floodSlider} from './flood-slider';
 
 
 describe('floodSlider', () => {
diff --git a/assets/src/scripts/components/map/index.js b/assets/src/scripts/components/map/index.js
index 634d0f283..73c3cf060 100644
--- a/assets/src/scripts/components/map/index.js
+++ b/assets/src/scripts/components/map/index.js
@@ -1,16 +1,16 @@
-import { select } from 'd3-selection';
-import { createStructuredSelector } from 'reselect';
-import { map as createMap, marker as createMarker, control, layerGroup } from 'leaflet';
-import { TiledMapLayer, dynamicMapLayer, Util, basemapLayer } from 'esri-leaflet/src/EsriLeaflet';
-import { link } from '../../lib/d3-redux';
+import {select} from 'd3-selection';
+import {createStructuredSelector} from 'reselect';
+import {map as createMap, marker as createMarker, control, layerGroup} from 'leaflet';
+import {TiledMapLayer, dynamicMapLayer, Util, basemapLayer} from 'esri-leaflet/src/EsriLeaflet';
+import {link} from '../../lib/d3-redux';
 import config from '../../config';
-import { FLOOD_EXTENTS_ENDPOINT, FLOOD_BREACH_ENDPOINT, FLOOD_LEVEE_ENDPOINT } from '../../web-services/flood-data';
-import { hasFloodData, getFloodExtent, getFloodStageHeight } from '../../selectors/flood-data-selector';
-import { Actions } from '../../store/site-store';
-import { floodSlider } from './flood-slider';
-import { createLegendControl, createFIMLegend, createNldiLegend } from './legend';
-import { addNldiLayers} from './nldiMapping';
-import { hasNldiData, getNldiDownstreamFlows, getNldiDownstreamSites, getNldiUpstreamFlows, getNldiUpstreamSites, getNldiUpstreamBasin}
+import {FLOOD_EXTENTS_ENDPOINT, FLOOD_BREACH_ENDPOINT, FLOOD_LEVEE_ENDPOINT} from '../../web-services/flood-data';
+import {hasFloodData, getFloodExtent, getFloodStageHeight} from '../../selectors/flood-data-selector';
+import {Actions} from '../../store';
+import {floodSlider} from './flood-slider';
+import {createLegendControl, createFIMLegend, createNldiLegend} from './legend';
+import {addNldiLayers} from './nldiMapping';
+import {hasNldiData, getNldiDownstreamFlows, getNldiDownstreamSites, getNldiUpstreamFlows, getNldiUpstreamSites, getNldiUpstreamBasin}
     from '../../selectors/nldi-data-selector';
 
 
diff --git a/assets/src/scripts/components/map/index.spec.js b/assets/src/scripts/components/map/index.spec.js
index 65025a661..07cfff665 100644
--- a/assets/src/scripts/components/map/index.spec.js
+++ b/assets/src/scripts/components/map/index.spec.js
@@ -1,6 +1,6 @@
-import { select } from 'd3-selection';
-import { attachToNode } from './index';
-import { configureStore } from '../../store/site-store';
+import {select} from 'd3-selection';
+import {attachToNode} from './index';
+import {configureStore} from '../../store';
 
 describe('map module', () => {
     let mapNode;
diff --git a/assets/src/scripts/d3-rendering/accessibility.spec.js b/assets/src/scripts/d3-rendering/accessibility.spec.js
index 2d6ce879f..76e7665e2 100644
--- a/assets/src/scripts/d3-rendering/accessibility.spec.js
+++ b/assets/src/scripts/d3-rendering/accessibility.spec.js
@@ -1,5 +1,5 @@
-import { addSVGAccessibility, addSROnlyTable } from './accessibility';
-import { select } from 'd3-selection';
+import {addSVGAccessibility, addSROnlyTable} from './accessibility';
+import {select} from 'd3-selection';
 
 describe('svgAccessibility tests', () => {
     describe('addAccessibility tests', () => {
diff --git a/assets/src/scripts/d3-rendering/legend.spec.js b/assets/src/scripts/d3-rendering/legend.spec.js
index 5ae110ede..1ce4f0688 100644
--- a/assets/src/scripts/d3-rendering/legend.spec.js
+++ b/assets/src/scripts/d3-rendering/legend.spec.js
@@ -1,7 +1,7 @@
 import {select, selectAll} from 'd3-selection';
 import {lineMarker, rectangleMarker, textOnlyMarker} from './markers';
 import {drawSimpleLegend} from './legend';
-import {configureStore} from '../store/site-store';
+import {configureStore} from '../store';
 import {drawTimeSeriesLegend} from '../components/dailyValueHydrograph/legend';
 
 describe('Legend module', () => {
diff --git a/assets/src/scripts/d3-rendering/loading-indicator.spec.js b/assets/src/scripts/d3-rendering/loading-indicator.spec.js
index 51083c087..429df83fe 100644
--- a/assets/src/scripts/d3-rendering/loading-indicator.spec.js
+++ b/assets/src/scripts/d3-rendering/loading-indicator.spec.js
@@ -1,4 +1,4 @@
-import { select } from 'd3-selection';
+import {select} from 'd3-selection';
 import {drawLoadingIndicator} from './loading-indicator';
 
 describe('loading indicator', () => {
diff --git a/assets/src/scripts/d3-rendering/markers.spec.js b/assets/src/scripts/d3-rendering/markers.spec.js
index 63d210ed5..a3cf63fc8 100644
--- a/assets/src/scripts/d3-rendering/markers.spec.js
+++ b/assets/src/scripts/d3-rendering/markers.spec.js
@@ -1,5 +1,5 @@
-import { select } from 'd3-selection';
-import { lineMarker, circleMarker, rectangleMarker, textOnlyMarker } from './markers';
+import {select} from 'd3-selection';
+import {lineMarker, circleMarker, rectangleMarker, textOnlyMarker} from './markers';
 
 describe('Markers module', () => {
 
diff --git a/assets/src/scripts/helpers.spec.js b/assets/src/scripts/helpers.spec.js
index 146cf3479..54fcb9237 100644
--- a/assets/src/scripts/helpers.spec.js
+++ b/assets/src/scripts/helpers.spec.js
@@ -1,5 +1,5 @@
-import { select } from 'd3-selection';
-import { registerTooltips, unregisterTooltips } from './helpers';
+import {select} from 'd3-selection';
+import {registerTooltips, unregisterTooltips} from './helpers';
 
 
 /**
diff --git a/assets/src/scripts/index.js b/assets/src/scripts/index.js
index 5dc6716f4..d9a383965 100644
--- a/assets/src/scripts/index.js
+++ b/assets/src/scripts/index.js
@@ -6,7 +6,7 @@ import wdfnviz from 'wdfn-viz';
 import {register} from './helpers';
 register();
 
-import {configureStore} from './store/site-store';
+import {configureStore} from './store';
 import {getParamString} from './url-params';
 
 import {attachToNode as EmbedComponent} from './components/embed';
diff --git a/assets/src/scripts/index.spec.js b/assets/src/scripts/index.spec.js
index 6e1ced577..b37f248ce 100644
--- a/assets/src/scripts/index.spec.js
+++ b/assets/src/scripts/index.spec.js
@@ -69,7 +69,7 @@ import './selectors/time-series-selector.spec';
 import './store/flood-data-reducer.spec';
 import './store/nldi-data-reducer.spec';
 import './store/flood-state-reducer.spec';
-import './store/site-store.spec';
+import './store/index.spec';
 import './store/observations-data-reducer.spec';
 import './store/observations-state-reducer.spec';
 import './store/series-reducer.spec';
diff --git a/assets/src/scripts/networks/index.js b/assets/src/scripts/networks/index.js
index 9caf0ae89..b6ccc2dd0 100644
--- a/assets/src/scripts/networks/index.js
+++ b/assets/src/scripts/networks/index.js
@@ -6,22 +6,18 @@ import wdfnviz from 'wdfn-viz';
 import {register} from '../helpers';
 register();
 
-import {configureStore} from './network-store';
+import {configureStore} from './store/network-store';
 import {getParamString} from '../url-params';
 
-import {attachToNode as NetworkMapComponent} from './components';
+import {attachToNode as NetworkMapComponent} from './network-component';
 
 const COMPONENTS = {
-    'network-map': NetworkMapComponent
+    'network': NetworkMapComponent
 };
 
 const load = function () {
     let nodes = document.getElementsByClassName('wdfn-component');
-    let store = configureStore({
-        ui: {
-            windowWidth: window.innerWidth
-        }
-    });
+    let store = configureStore();
     for (let node of nodes) {
         // If options is specified on the node, expect it to be a JSON string.
         // Otherwise, use the dataset attributes as the component options.
diff --git a/assets/src/scripts/networks/index.spec.js b/assets/src/scripts/networks/index.spec.js
new file mode 100644
index 000000000..f9d07cf98
--- /dev/null
+++ b/assets/src/scripts/networks/index.spec.js
@@ -0,0 +1,5 @@
+import './web-services/network-data.spec';
+import './store/network-data-reducer.spec';
+import './selectors/network-data-selector.spec';
+import './network-component/index.spec';
+import './network-component/network-legend.spec';
\ No newline at end of file
diff --git a/assets/src/scripts/networks/components/index.js b/assets/src/scripts/networks/network-component/index.js
similarity index 61%
rename from assets/src/scripts/networks/components/index.js
rename to assets/src/scripts/networks/network-component/index.js
index 4a7a9b006..998a6b10d 100644
--- a/assets/src/scripts/networks/components/index.js
+++ b/assets/src/scripts/networks/network-component/index.js
@@ -1,30 +1,30 @@
-import { select } from 'd3-selection';
-import { createStructuredSelector } from 'reselect';
-import { link } from '../../lib/d3-redux';
+import {select} from 'd3-selection';
+import {createStructuredSelector} from 'reselect';
+import {link} from '../../lib/d3-redux';
 import config from '../../config';
 
-import { Actions } from '../network-store';
-import { createLegendControl, createNetworkLegend } from './network-legend';
-import { addNetworkLayers} from './network-mapping';
-import { hasNetworkData, getNetworkSites}
-    from '../network-data-selector';
+import {Actions} from '../store/network-store';
+import {createLegendControl, createNetworkLegend} from './network-legend';
+import {addNetworkLayers} from './network-elements';
+import {hasNetworkData, getNetworkSites}
+    from '../selectors/network-data-selector';
 
 /*
  * Creates a network map
  */
-const networkMap = function(node, {extent}, store) {
+const networkMap = function(node, extent, store) {
 
-    let gray = new L.layerGroup();
-    L.esri.basemapLayer('Gray').addTo(gray);
+    let gray = new window.L.layerGroup();
+    window.L.esri.basemapLayer('Gray').addTo(gray);
 
     if (config.HYDRO_ENDPOINT) {
-        gray.addLayer(new L.esri.TiledMapLayer({url: config.HYDRO_ENDPOINT,
+        gray.addLayer(new window.L.esri.TiledMapLayer({url: config.HYDRO_ENDPOINT,
             maxZoom: 22,
             maxNativeZoom: 19}));
     }
 
     // Create map on node
-    const map = new L.Map('network-map', {
+    const map = new window.L.Map('network-map', {
         center: [0, 0],
         zoom: 1,
         scrollWheelZoom: false,
@@ -42,7 +42,6 @@ const networkMap = function(node, {extent}, store) {
         map.scrollWheelZoom.disable();
     });
 
-
     let legendControl = createLegendControl({
         position: 'bottomright'
     });
@@ -55,24 +54,21 @@ const networkMap = function(node, {extent}, store) {
     //add additional baselayer
     var baseLayers = {
         'Grayscale': gray,
-        'Satellite': new L.esri.basemapLayer('ImageryFirefly')
+        'Satellite': new window.L.esri.basemapLayer('ImageryFirefly')
     };
 
     //add layer control
-    L.control.layers(baseLayers).addTo(map);
+    window.L.control.layers(baseLayers).addTo(map);
 
     // Add the ESRI World Hydro Reference Overlay
     if (config.HYDRO_ENDPOINT) {
-        map.addLayer(new L.esri.TiledMapLayer({url: config.HYDRO_ENDPOINT}));
+        map.addLayer(new window.L.esri.TiledMapLayer({url: config.HYDRO_ENDPOINT}));
     }
 
-    // // Add a marker at the site location
-    // createMarker([latitude, longitude]).addTo(map);
-
     /*
-     * Creates the NLDI legend if NLDI data is available, otherwise removes the NLDI legend if it exists.
+     * Creates the Network legend if Network data is available, otherwise removes the Network legend if it exists.
      * @param {HTMLElement} node - element where the map is rendered
-     * @param {Boolean} isNldiAvailable
+     * @param {Boolean} isNetworkAvailable
      */
     const addNetworkLegend = function(node, isNetworkAvailable) {
         createNetworkLegend(legendControl, isNetworkAvailable);
@@ -96,6 +92,6 @@ export const attachToNode = function(store, node, {networkcd, extent}) {
     store.dispatch(Actions.retrieveNetworkData(networkcd));
 
     select(node)
-        .call(networkMap, {extent}, store);
+        .call(networkMap, extent, store);
 };
 
diff --git a/assets/src/scripts/networks/network-component/index.spec.js b/assets/src/scripts/networks/network-component/index.spec.js
new file mode 100644
index 000000000..3d50aeace
--- /dev/null
+++ b/assets/src/scripts/networks/network-component/index.spec.js
@@ -0,0 +1,136 @@
+import {select} from 'd3-selection';
+import {attachToNode} from './index';
+import {configureStore} from '../store/network-store';
+
+describe('network map module', () => {
+    let mapNode;
+    let store;
+
+    beforeEach(() => {
+        jasmine.Ajax.install();
+        let mapContainer = select('body')
+            .append('div')
+                .attr('id', 'map');
+        mapContainer.append('div').attr('id', 'network-map');
+        mapNode = document.getElementById('map');
+        select('body')
+            .append('input')
+                .attr('id', 'table-search');
+        select('body')
+            .append('div')
+                .attr('id', 'link-list');
+    });
+
+    afterEach(() => {
+        select('#map').remove();
+        jasmine.Ajax.uninstall();
+    });
+
+
+    describe('Map creation without Network maps', () => {
+        beforeEach(() => {
+            store = configureStore();
+            attachToNode(store, mapNode, {
+                networkcd: 'AHS',
+                extent: '[-93.078075, 34.513375, -92.986325, 34.588425]'
+            });
+        });
+
+        it('Should create a leaflet map within the mapNode with', () => {
+            expect(select(mapNode).selectAll('.leaflet-container').size()).toBe(1);
+        });
+
+        it('Should not create an overlay layer', () => {
+            expect(select(mapNode).selectAll('.leaflet-overlay-pane img').size()).toBe(0);
+        });
+
+        it('Should create a legend control', () => {
+            expect(select(mapNode).selectAll('.legend').size()).toBe(1);
+        });
+
+        it('Should create not create Network Legend', () => {
+            expect(select(mapNode).select('#network-legend-list').size()).toBe(0);
+        });
+
+        it('Should create a leaf-control-layers class', () => {
+            expect(select(mapNode).selectAll('.leaflet-control-layers').size()).toBe(1);
+        });
+
+    });
+
+
+    describe('Map network information', () => {
+        beforeEach(() => {
+            store = configureStore({
+                networkData: {
+                    networkSites: [{
+                        type: 'Feature',
+                        id: 'USGS-343048093030401',
+                        geometry: {
+                            type: 'Point',
+                            coordinates: [
+                                -93.0511417,
+                                34.513375
+                            ]
+                        },
+                        properties: {
+                            agency: 'U.S. Geological Survey',
+                            monitoringLocationNumber: '343048093030401',
+                            monitoringLocationName: '02S19W33CBD1 Hot Springs',
+                            monitoringLocationType: 'Well',
+                            district: 'Arkansas',
+                            state: 'Arkansas',
+                            county: 'Garland County',
+                            country: 'US',
+                            monitoringLocationAltitudeLandSurface: '749',
+                            altitudeMethod: 'Interpolated from Digital Elevation Model',
+                            altitudeAccuracy: '4.3',
+                            altitudeDatum: 'North American Vertical Datum of 1988',
+                            nationalAquifer: 'Other aquifers',
+                            localAquifer: 'Hot Springs Sandstone',
+                            localAquiferType: 'Unconfined single aquifer',
+                            wellDepth: '336.5',
+                            holeDepth: '336.5',
+                            holeDepthSource: 'L',
+                            agencyCode: 'USGS',
+                            districtCode: '05',
+                            stateFIPS: 'US:05',
+                            countyFIPS: 'US:05:051',
+                            countryFIPS: 'US',
+                            hydrologicUnit: '080401010804',
+                            monitoringLocationUrl: 'https://waterdata.usgs.gov/monitoring-location/343048093030401'
+                        },
+                        links: [
+                            {
+                                rel: 'self',
+                                type: 'application/geo+json',
+                                title: 'This document as GeoJSON',
+                                href: 'https://labs.waterdata.usgs.gov/api/observations/collections/AHS/items/USGS-343048093030401?f=json'
+                            },
+                            {
+                                rel: 'collection',
+                                type: 'application/json',
+                                title: 'Arkansas Hot Springs National Park Network',
+                                href: 'https://labs.waterdata.usgs.gov/api/observations/collections/AHS?f=json'
+                            }
+                        ]
+                    }
+                    ]
+                }
+            });
+            attachToNode(store, mapNode, {
+                networkcd: 'AHS',
+                extent: '[-93.078075, 34.513375, -92.986325, 34.588425]'
+            });
+        });
+
+        it('Should create Network layers', () => {
+            expect(select(mapNode).selectAll('.leaflet-overlay-pane svg g').size()).toBe(1);
+        });
+
+        it('Should create a Network Legend', () => {
+            expect(select(mapNode).select('#network-legend-list').size()).toBe(1);
+        });
+
+    });
+});
diff --git a/assets/src/scripts/networks/components/network-mapping.js b/assets/src/scripts/networks/network-component/network-elements.js
similarity index 63%
rename from assets/src/scripts/networks/components/network-mapping.js
rename to assets/src/scripts/networks/network-component/network-elements.js
index ac2981096..da519ff45 100644
--- a/assets/src/scripts/networks/components/network-mapping.js
+++ b/assets/src/scripts/networks/network-component/network-elements.js
@@ -1,4 +1,4 @@
-import Tabulator from 'tabulator-tables/dist/js/tabulator.js';
+import List from 'list.js';
 export const markerFillColor = '#ff7800';
 export const markerFillOpacity = 0.8;
 
@@ -8,9 +8,7 @@ export const markerFillOpacity = 0.8;
  *
  * @param {L.map} map The leaflet map to which the overlay should be added
  * @param newtorkSites network site geojson data
-
  */
-
 export const addNetworkLayers = function (map, networkSites) {
 
     const geojsonMarkerOptions = {
@@ -34,10 +32,10 @@ export const addNetworkLayers = function (map, networkSites) {
     };
 
     const getPointDataLayer = function (data, markerOptions) {
-        return L.geoJson(data, {
+        return window.L.geoJson(data, {
             onEachFeature: onEachPointFeatureAddPopUp,
             pointToLayer: function (feature, latlng) {
-                return L.circleMarker(latlng, markerOptions);
+                return window.L.circleMarker(latlng, markerOptions);
             }
         });
     };
@@ -51,40 +49,27 @@ export const addNetworkLayers = function (map, networkSites) {
 
     if(networkSites.length > 0 && networkSites.length < 10000) {
         if (networkSites.length > 50) {
-            const markers = L.markerClusterGroup({chunkedLoading: true});
+            const markers = window.L.markerClusterGroup({chunkedLoading: true});
             markers.addLayer(networkLayer);
             map.addLayer(markers);
         } else {
             map.addLayer(networkLayer);
         }
 
-        const tableEl = document.getElementById('link-list');
-        const table = new Tabulator(tableEl, {
-            data: listValues,
-            height: '400px',
-            layout: 'fitColumns',
-            responsiveLayout: 'hide',
-            tooltips: true,
-            pagination: 'local',
-            paginationSize: 30,
-            movableColumns: true,
-            resizableRows: true,
-            initialSort: [
-                {column: 'name', dir: 'asc'}
-            ],
-            columns: [
-                {title: 'Name', field: 'name'},
-                {title: 'Link', field: 'link', formatter: 'link', formatterParams:{
-                    labelField:'link'
-                }}
-            ]
-        });
+        const valueNames = ['name', 'link',  { name: 'link', attr: 'href' }];
+        const options = {
+            valueNames: valueNames,
+            item: '<tr><td class="name"></td><td><a class="link"></a></td></tr>',
+            page: 10,
+            pagination: [{
+                left: 1,
+                right: 1,
+                innerWindow: 2,
+                outerWindow: 1
+            }]
+        };
+        new List('link-list', options, listValues);
 
-        const searchValue = document.getElementById('table-search');
-
-        searchValue.addEventListener('keyup', function() {
-            table.setFilter('name', 'like', searchValue.value);
-        });
 
 
     } else{
diff --git a/assets/src/scripts/networks/components/network-legend.js b/assets/src/scripts/networks/network-component/network-legend.js
similarity index 72%
rename from assets/src/scripts/networks/components/network-legend.js
rename to assets/src/scripts/networks/network-component/network-legend.js
index 22df44ce1..dd7c3c09f 100644
--- a/assets/src/scripts/networks/components/network-legend.js
+++ b/assets/src/scripts/networks/network-component/network-legend.js
@@ -1,37 +1,33 @@
 // Creates a Leaflet legend control. If the legend contains FIM information than the expand/collapse control
 // will be visible
-
-import { select } from 'd3-selection';
+import {select} from 'd3-selection';
 import config from '../../config';
-import { mediaQuery } from '../../utils';
-import { markerFillColor, markerFillOpacity} from './network-mapping';
+import {mediaQuery} from '../../utils';
+import {markerFillColor, markerFillOpacity} from './network-elements';
 
 /*
  * @param {Object} - options allowed for a standard Leaflet Control.
- * @return L.Control containing the legend control
+ * @return window.L.Control containing the legend control
  */
 export const createLegendControl = function(options) {
-    let legendControl = L.control(options);
+    let legendControl = window.L.control(options);
 
     legendControl.onAdd = function() {
-        let container = L.DomUtil.create('div', 'legend');
+        let container = window.L.DomUtil.create('div', 'legend');
 
-        let buttonContainer = L.DomUtil.create('div', 'legend-expand-container', container);
+        let buttonContainer = window.L.DomUtil.create('div', 'legend-expand-container', container);
         // Only make the expand button available if FIM legends are added
         buttonContainer.setAttribute('hidden', true);
-        let buttonLabel = L.DomUtil.create('span', '', buttonContainer);
+        let buttonLabel = window.L.DomUtil.create('span', '', buttonContainer);
         buttonLabel.innerHTML = 'Legend';
-        let expandButton = L.DomUtil.create('button', 'legend-expand usa-button-secondary', buttonContainer);
+        let expandButton = window.L.DomUtil.create('button', 'legend-expand usa-button-secondary', buttonContainer);
         expandButton.innerHTML = '<i class="fas fa-compress"></i>';
         expandButton.title = 'Hide legend';
 
-        let legendListContainer = L.DomUtil.create('div', 'legend-list-container', container);
-        let legendList = L.DomUtil.create('ul', 'usa-list--unstyled', legendListContainer);
-        legendList.id = 'network-legend-list';
-        legendList.innerHTML = `<li><img src="${config.STATIC_URL}/images/marker-icon.png" alt="Map marker"/><span>Monitoring Location</span> </li>`;
+        let legendListContainer = window.L.DomUtil.create('div', 'legend-list-container', container);
 
         // Set up click handler for the expandButton
-        L.DomEvent.on(expandButton, 'click', function() {
+        window.L.DomEvent.on(expandButton, 'click', function() {
             if (expandButton.title === 'Hide legend') {
                 expandButton.innerHTML = '<i class="fas fa-expand"></i>';
                 expandButton.title = 'Show legend';
@@ -52,7 +48,7 @@ export const createLegendControl = function(options) {
 
 /**
  * Compresses the legend on smaller devices like phones
- * @param {L.Control} legendControl - Leaflet legend control
+ * @param {window.L.Control} legendControl - Leaflet legend control
  */
 const compressLegendOnSmallDevices = function(legendControl) {
     const legendContainer = select(legendControl.getContainer());
@@ -75,7 +71,7 @@ const compressLegendOnSmallDevices = function(legendControl) {
 
 /**
  * Creates the network legend if network data is available, otherwise removes the network legend if it exists.
- * @param {L.Control} legendControl - Leaflet legend control
+ * @param {window.L.Control} legendControl - Leaflet legend control
  * @param {Boolean} is network available
  */
 export const createNetworkLegend = function(legendControl, isNetworkAvailable) {
diff --git a/assets/src/scripts/networks/network-component/network-legend.spec.js b/assets/src/scripts/networks/network-component/network-legend.spec.js
new file mode 100644
index 000000000..f134035db
--- /dev/null
+++ b/assets/src/scripts/networks/network-component/network-legend.spec.js
@@ -0,0 +1,87 @@
+import {select} from 'd3-selection';
+
+import {createLegendControl, createNetworkLegend} from './network-legend';
+
+
+describe('component/map/legend module', () => {
+    let legendControl;
+    let map;
+
+    beforeEach(() => {
+        jasmine.Ajax.install();
+        select('body').append('div')
+            .attr('id', 'map');
+        map = window.L.map('map', {
+            center: [43.0, -100.0],
+            zoom: 5
+        });
+    });
+
+    afterEach(() => {
+        select('#map').remove();
+        jasmine.Ajax.uninstall();
+    });
+
+    describe('createLegendControl', () => {
+        let legendContainer, containerSelect;
+        beforeEach(() => {
+            legendControl = createLegendControl({});
+            legendControl.addTo(map);
+
+            legendContainer = legendControl.getContainer();
+            containerSelect = select(legendContainer);
+        });
+
+        it('Creates the expected DOM elements', () => {
+            expect(legendContainer).toBeDefined();
+            expect(containerSelect.selectAll('.legend-expand').size()).toBe(1);
+            expect(containerSelect.selectAll('.legend-list-container').size()).toBe(1);
+        });
+
+        it('The legend expand button is not visible and that the legend list is visible', () => {
+            expect(containerSelect.selectAll('.legend-expand-container').attr('hidden')).toBeTruthy();
+            expect(containerSelect.selectAll('.legend-list-container').attr('hidden')).toBeNull();
+        });
+
+        it('Clicking the expand button once hides the legend list', () => {
+            let expandButton = containerSelect.select('.legend-expand');
+            expandButton.dispatch('click');
+
+            expect(containerSelect.selectAll('.legend-list-container').attr('hidden')).toBeTruthy();
+            expect(expandButton.attr('title')).toContain('Show');
+        });
+
+        it('Clicking the expand button a second time shows the legend list', () => {
+            let expandButton = containerSelect.select('.legend-expand');
+            expandButton.dispatch('click');
+            expandButton.dispatch('click');
+
+            expect(containerSelect.selectAll('.legend-list-container').attr('hidden')).toBeNull();
+            expect(expandButton.attr('title')).toContain('Hide');
+        });
+    });
+
+
+
+    describe('createNetworkLegend', () => {
+        beforeEach(() => {
+            legendControl = createLegendControl({});
+            legendControl.addTo(map);
+
+            createNetworkLegend(legendControl, true);
+        });
+
+        it('createNetworkLegend with Network available true makes the expand button visible', () =>  {
+            expect(select(legendControl.getContainer()).select('.legend-expand-container').attr('hidden')).toBeNull();
+        });
+
+        it('createNetworkLegend with Network available add the Network legend list to the control', () => {
+            expect(select(legendControl.getContainer()).select('#network-legend-list').size()).toBe(1);
+        });
+
+        it('Calling createNetworkLegend a second time with available set to false cause the Network legend list to be removed', () => {
+            createNetworkLegend(legendControl, false);
+            expect(select(legendControl.getContainer()).select('#network-legend-list').size()).toBe(0);
+        });
+    });
+});
diff --git a/assets/src/scripts/networks/network-store.js b/assets/src/scripts/networks/network-store.js
deleted file mode 100644
index dba4db503..000000000
--- a/assets/src/scripts/networks/network-store.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import {fetchNetworkSites} from '../networks/network-data';
-
-import {uiReducer as ui} from '../store/ui-reducer';
-import {networkDataReducer as networkData} from './network-data-reducer';
-import {configureReduxStore} from '../store';
-
-export const Actions = {
-    retrieveNetworkData(networkCd) {
-        return function(dispatch) {
-            const networkSites = fetchNetworkSites(networkCd);
-
-            return Promise.all( [networkSites]
-            ).then(function(data){
-                const [networkSites] = data;
-                dispatch(Actions.setNetworkFeatures(networkSites));
-            });
-        };
-    },
-    setNetworkFeatures(networkSites) {
-         return {
-            type: 'SET_NETWORK_FEATURES',
-            networkSites
-        };
-    },
-    resizeUI(windowWidth, width) {
-        return {
-            type: 'RESIZE_UI',
-            windowWidth,
-            width
-        };
-    }
-};
-
-const reducers = {
-    ui,
-    networkData
-};
-
-export const configureStore = function(initialState){
-
-     initialState = {
-         networkData: {
-             networkSites: []
-         },
-         ui: {
-             windowWidth: 1024,
-             width: 800
-         },
-         ...initialState
-     };
-
-    return configureReduxStore(initialState, reducers);
-};
\ No newline at end of file
diff --git a/assets/src/scripts/networks/network-data-selector.js b/assets/src/scripts/networks/selectors/network-data-selector.js
similarity index 86%
rename from assets/src/scripts/networks/network-data-selector.js
rename to assets/src/scripts/networks/selectors/network-data-selector.js
index 7605fe5ee..f3db8ecde 100644
--- a/assets/src/scripts/networks/network-data-selector.js
+++ b/assets/src/scripts/networks/selectors/network-data-selector.js
@@ -1,4 +1,4 @@
-import { createSelector } from 'reselect';
+import {createSelector} from 'reselect';
 
 export const getNetworkSites = state => state.networkData.networkSites;
 
diff --git a/assets/src/scripts/networks/network-data-selector.spec.js b/assets/src/scripts/networks/selectors/network-data-selector.spec.js
similarity index 73%
rename from assets/src/scripts/networks/network-data-selector.spec.js
rename to assets/src/scripts/networks/selectors/network-data-selector.spec.js
index 4c2f72d94..9aea555ad 100644
--- a/assets/src/scripts/networks/network-data-selector.spec.js
+++ b/assets/src/scripts/networks/selectors/network-data-selector.spec.js
@@ -5,8 +5,8 @@ import {
 
 describe('network-data-selector', () => {
 
-    describe('getNldiUpstreamFlows', () => {
-        it('if upstream flows is empty, empty array is returned', () => {
+    describe('getNetworkSites', () => {
+        it('if network is empty, empty array is returned', () => {
             expect(getNetworkSites({
                 networkData: {
                     networkSites: []
@@ -14,7 +14,7 @@ describe('network-data-selector', () => {
             })).toEqual([]);
         });
 
-        it('if upstream flows has data, the data is returned', () => {
+        it('if network sites, the data is returned', () => {
             expect(getNetworkSites({
                 networkData: {
                     networkSites: [1,2,3]
@@ -25,7 +25,7 @@ describe('network-data-selector', () => {
 
 
     describe('hasNetworkData', () => {
-        it('if all nldi data is empty, return false', () => {
+        it('if all network data is empty, return false', () => {
             expect(hasNetworkData({
                 networkData: {
                     networkSites: []
@@ -33,7 +33,7 @@ describe('network-data-selector', () => {
             })).toEqual(false);
         });
 
-        it('if even one nldi data has data, return true', () => {
+        it('if even one network data has data, return true', () => {
             expect(hasNetworkData({
                  networkData: {
                     networkSites: [1]
diff --git a/assets/src/scripts/networks/network-data-reducer.js b/assets/src/scripts/networks/store/network-data-reducer.js
similarity index 100%
rename from assets/src/scripts/networks/network-data-reducer.js
rename to assets/src/scripts/networks/store/network-data-reducer.js
diff --git a/assets/src/scripts/networks/network-data-reducer.spec.js b/assets/src/scripts/networks/store/network-data-reducer.spec.js
similarity index 97%
rename from assets/src/scripts/networks/network-data-reducer.spec.js
rename to assets/src/scripts/networks/store/network-data-reducer.spec.js
index a7af86545..01a19358b 100644
--- a/assets/src/scripts/networks/network-data-reducer.spec.js
+++ b/assets/src/scripts/networks/store/network-data-reducer.spec.js
@@ -1,11 +1,11 @@
-import { networkDataReducer } from './network-data-reducer';
+import {networkDataReducer} from './network-data-reducer';
 
 describe('network-data-reducer', () => {
 
     describe('SET_NETWORK_FEATURES', () => {
-        it('should handle setting the nldiData', () => {
+        it('should handle setting the network data', () => {
             expect(networkDataReducer({}, {
-                type: 'SET_NLDI_FEATURES',
+                type: 'SET_NETWORK_FEATURES',
                     networkSites: [
                         {
                             type: 'Feature',
diff --git a/assets/src/scripts/networks/store/network-store.js b/assets/src/scripts/networks/store/network-store.js
new file mode 100644
index 000000000..140bd123a
--- /dev/null
+++ b/assets/src/scripts/networks/store/network-store.js
@@ -0,0 +1,57 @@
+import {applyMiddleware, createStore, combineReducers, compose} from 'redux';
+import {default as thunk} from 'redux-thunk';
+
+import {fetchNetworkSites} from '../web-services/network-data';
+
+import {networkDataReducer as networkData} from './network-data-reducer';
+
+export const Actions = {
+    retrieveNetworkData(networkCd) {
+        return function(dispatch) {
+            const networkSites = fetchNetworkSites(networkCd);
+
+            return Promise.all( [networkSites]
+            ).then(function(data){
+                const [networkSites] = data;
+                dispatch(Actions.setNetworkFeatures(networkSites));
+            });
+        };
+    },
+    setNetworkFeatures(networkSites) {
+         return {
+            type: 'SET_NETWORK_FEATURES',
+            networkSites
+        };
+    }
+};
+
+const appReducer = combineReducers({
+    networkData
+});
+
+const MIDDLEWARES = [thunk];
+
+export const configureStore = function (initialState) {
+    initialState = {
+        networkData: {
+            networkSites: []
+        },
+        ...initialState
+    };
+
+    let enhancers;
+    if (window.__REDUX_DEVTOOLS_EXTENSION__) {
+        enhancers = compose(
+            applyMiddleware(...MIDDLEWARES),
+            window.__REDUX_DEVTOOLS_EXTENSION__({serialize: true})
+        );
+    } else {
+        enhancers = applyMiddleware(...MIDDLEWARES);
+    }
+
+    return createStore(
+        appReducer,
+        initialState,
+        enhancers
+    );
+};
\ No newline at end of file
diff --git a/assets/src/scripts/networks/network-data.js b/assets/src/scripts/networks/web-services/network-data.js
similarity index 89%
rename from assets/src/scripts/networks/network-data.js
rename to assets/src/scripts/networks/web-services/network-data.js
index c53fd90ac..08d294fe9 100644
--- a/assets/src/scripts/networks/network-data.js
+++ b/assets/src/scripts/networks/web-services/network-data.js
@@ -1,5 +1,5 @@
-import config from '../config';
-import { get } from '../ajax';
+import config from '../../config';
+import {get} from '../../ajax';
 
 const networkUrl = config.NETWORK_ENDPOINT;
 
@@ -20,7 +20,7 @@ const fetchNetworkData = function(networkQuery, networkCd) {
         });
 };
 
-// nldi webservice calls
+// network webservice calls
 export const fetchNetworkSites = function(networkCd) {
     const networkQuery = `${networkUrl}/${networkCd}/items`;
     return fetchNetworkData(networkQuery, networkCd);
diff --git a/assets/src/scripts/networks/network-data.spec.js b/assets/src/scripts/networks/web-services/network-data.spec.js
similarity index 99%
rename from assets/src/scripts/networks/network-data.spec.js
rename to assets/src/scripts/networks/web-services/network-data.spec.js
index d803d1b51..7bce87fe5 100644
--- a/assets/src/scripts/networks/network-data.spec.js
+++ b/assets/src/scripts/networks/web-services/network-data.spec.js
@@ -99,6 +99,7 @@ const MOCK_NETWORK_FEATURE = `
             "title": "Arkansas Hot Springs National Park Network",
             "href": "https://labs.waterdata.usgs.gov/api/observations/collections/AHS?f=json"
         }]
-    ]}
+    }]
 }
-`;
\ No newline at end of file
+`
+;
\ No newline at end of file
diff --git a/assets/src/scripts/schema.js b/assets/src/scripts/schema.js
index 1e215085c..394f224b3 100644
--- a/assets/src/scripts/schema.js
+++ b/assets/src/scripts/schema.js
@@ -1,6 +1,6 @@
 import memoize from 'fast-memoize';
-import { normalize as normalizr, schema } from 'normalizr';
-import { replaceHtmlEntities } from './utils';
+import {normalize as normalizr, schema} from 'normalizr';
+import {replaceHtmlEntities} from './utils';
 
 
 // sourceInfo schema
diff --git a/assets/src/scripts/schema.spec.js b/assets/src/scripts/schema.spec.js
index a44eedfda..b829664d0 100644
--- a/assets/src/scripts/schema.spec.js
+++ b/assets/src/scripts/schema.spec.js
@@ -1,4 +1,4 @@
-import { normalize } from './schema';
+import {normalize} from './schema';
 
 
 describe('Normalizr schema', () => {
diff --git a/assets/src/scripts/store/flood-data-reducer.spec.js b/assets/src/scripts/store/flood-data-reducer.spec.js
index dc0fae7f7..328eeda07 100644
--- a/assets/src/scripts/store/flood-data-reducer.spec.js
+++ b/assets/src/scripts/store/flood-data-reducer.spec.js
@@ -1,4 +1,4 @@
-import { floodDataReducer } from './flood-data-reducer';
+import {floodDataReducer} from './flood-data-reducer';
 
 describe('flood-data-reducer', () => {
 
diff --git a/assets/src/scripts/store/flood-state-reducer.spec.js b/assets/src/scripts/store/flood-state-reducer.spec.js
index eb043aece..7e98b8b5e 100644
--- a/assets/src/scripts/store/flood-state-reducer.spec.js
+++ b/assets/src/scripts/store/flood-state-reducer.spec.js
@@ -1,4 +1,4 @@
-import { floodStateReducer } from './flood-state-reducer';
+import {floodStateReducer} from './flood-state-reducer';
 
 describe('flood-state-reducer', () => {
 
diff --git a/assets/src/scripts/store/index.js b/assets/src/scripts/store/index.js
index 67f55eafb..9b27de596 100644
--- a/assets/src/scripts/store/index.js
+++ b/assets/src/scripts/store/index.js
@@ -1,39 +1,563 @@
-import {createStore, combineReducers, compose, applyMiddleware} from 'redux';
+import find from 'lodash/find';
+import findKey from 'lodash/findKey';
+import last from 'lodash/last';
+import {DateTime} from 'luxon';
+import {applyMiddleware, createStore, combineReducers, compose} from 'redux';
 import {default as thunk} from 'redux-thunk';
 
-const createReducer = function(asyncReducers) {
-  return combineReducers({
-    ...asyncReducers
-  });
+import {normalize} from '../schema';
+import {calcStartTime, sortedParameters} from '../utils';
+
+import {getCurrentParmCd, getCurrentDateRange, hasTimeSeries, getTsRequestKey, getRequestTimeRange,
+    getCustomTimeRange, getIanaTimeZone} from '../selectors/time-series-selector';
+
+import {fetchFloodFeatures, fetchFloodExtent} from '../web-services/flood-data';
+import {getPreviousYearTimeSeries, getTimeSeries, queryWeatherService} from '../web-services/models';
+import {fetchNldiUpstreamSites, fetchNldiDownstreamSites, fetchNldiDownstreamFlow, fetchNldiUpstreamFlow, fetchNldiUpstreamBasin} from '../web-services/nldi-data';
+import {fetchTimeSeries} from '../web-services/observations';
+import {fetchSiteStatistics} from '../web-services/statistics-data';
+
+import {floodDataReducer as floodData} from './flood-data-reducer';
+import {floodStateReducer as floodState} from './flood-state-reducer';
+import {nldiDataReducer as nldiData} from './nldi-data-reducer';
+import {observationsDataReducer as observationsData} from './observations-data-reducer';
+import {observationsStateReducer as observationsState} from './observations-state-reducer';
+import {seriesReducer as series} from './series-reducer';
+import {statisticsDataReducer as statisticsData} from './statistics-data-reducer';
+import {timeSeriesStateReducer as timeSeriesState} from './time-series-state-reducer';
+import {uiReducer as ui} from './ui-reducer';
+
+const GAGE_HEIGHT_CD = '00065';
+/*
+ * Helper functions
+ */
+const getLatestValue = function(collection, parmCd) {
+    let parmVar = findKey(collection.variables, (varValue) => {
+        return varValue.variableCode.value === parmCd;
+    });
+    let parmTimeSeries = findKey(collection.timeSeries, (ts) => {
+        return ts.variable === parmVar;
+    });
+    let points = parmTimeSeries ? collection.timeSeries[parmTimeSeries].points : [];
+    return points.length ? last(points).value : null;
 };
 
-const MIDDLEWARES = [thunk];
 
-export const configureReduxStore = function(initialState, reducers) {
+/*
+ * @param {Object} timeSeries - keys are time series id
+ * @param {Object} variables  - keys are the variable id
+ */
+const getCurrentVariableId = function(timeSeries, variables) {
+    const tsVariablesWithData = Object.values(timeSeries)
+        .filter((ts) => ts.points.length)
+        .map((ts) => variables[ts.variable]);
+    const sortedVarsWithData = sortedParameters(tsVariablesWithData);
+    if (sortedVarsWithData.length) {
+        return sortedVarsWithData[0].oid;
+    } else {
+        const sortedVars = sortedParameters(Object.values(variables));
+        return sortedVars.length ? sortedVars[0].oid : '';
+    }
+};
+
+export const Actions = {
+    retrieveLocationTimeZone(latitude, longitude) {
+        return function(dispatch) {
+            return queryWeatherService(latitude, longitude).then(
+                resp => {
+                    const tzIANA = resp.properties.timeZone || null; // set to time zone to null if unavailable
+                    dispatch(Actions.setLocationIanaTimeZone(tzIANA));
+                },
+                () => {
+                    dispatch(Actions.setLocationIanaTimeZone(null));
+                }
+            );
+        };
+    },
+    retrieveTimeSeries(siteno, params=null) {
+        return function (dispatch, getState) {
+            const currentState = getState();
+            const requestKey = getTsRequestKey('current', 'P7D')(currentState);
+            dispatch(Actions.addTimeSeriesLoading([requestKey]));
+            return getTimeSeries({sites: [siteno], params}).then(
+                series => {
+                    const collection = normalize(series, requestKey);
+
+                    // Get the start/end times of this request's range.
+                    const notes = collection.queryInfo[requestKey].notes;
+                    const endTime = notes.requestDT;
+                    const startTime = calcStartTime('P7D', endTime, 'local');
+
+                    // Trigger a call to get last year's data
+                    dispatch(Actions.retrieveCompareTimeSeries(siteno, 'P7D', startTime, endTime));
 
-  let enhancers;
-  if (window.__REDUX_DEVTOOLS_EXTENSION__) {
-      enhancers = compose(
-          applyMiddleware(...MIDDLEWARES),
-          window.__REDUX_DEVTOOLS_EXTENSION__({serialize: true})
-      );
-  } else {
-      enhancers = applyMiddleware(...MIDDLEWARES);
-  }
+                    // Update the series data for the 'current' series
+                    dispatch(Actions.addSeriesCollection('current', collection));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
 
-  const store = createStore(createReducer(reducers), initialState, enhancers);
+                    // Update the application state
+                    dispatch(Actions.toggleTimeSeries('current', true));
+                    const variable = getCurrentVariableId(collection.timeSeries || {}, collection.variables || {});
+                    dispatch(Actions.setCurrentVariable(variable));
+                    dispatch(Actions.setGageHeight(getLatestValue(collection, GAGE_HEIGHT_CD)));
+                },
+                () => {
+                    dispatch(Actions.resetTimeSeries(getTsRequestKey('current', 'P7D')(currentState)));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
 
-  // Add a dictionary to keep track of the registered async reducers
-  store.asyncReducers = {};
+                    dispatch(Actions.toggleTimeSeries('current', false));
+                }
+            );
+        };
+    },
+    retrieveCompareTimeSeries(site, period, startTime, endTime) {
+        return function (dispatch, getState) {
+            const requestKey = getTsRequestKey('compare', period)(getState());
+            dispatch(Actions.addTimeSeriesLoading([requestKey]));
+            return getPreviousYearTimeSeries({site, startTime, endTime}).then(
+                series => {
+                    const collection = normalize(series, requestKey);
+                    dispatch(Actions.addSeriesCollection(requestKey, collection));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                },
+                () => {
+                    dispatch(Actions.resetTimeSeries(getTsRequestKey('compare', period)(getState())));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                }
+            );
+        };
+    },
+    retrieveMedianStatistics(site) {
+        return function(dispatch) {
+            return fetchSiteStatistics({site, statType: 'median'}).then(
+                stats => {
+                    dispatch(Actions.addMedianStats(stats));
+                }
+            );
+        };
+    },
 
-  // Create an inject reducer function
-  // This function adds the async reducer, and creates a new combined reducer
-  store.injectReducer = (key, asyncReducer) => {
-    store.asyncReducers[key] = asyncReducer;
-    store.replaceReducer(createReducer(store.asyncReducers));
-  };
+    retrieveCustomTimePeriodTimeSeries(site, parameterCd, period) {
+        return function(dispatch, getState) {
+            const state = getState();
+            const parmCd = parameterCd;
+            const requestKey = getTsRequestKey('current', 'custom', parmCd)(state);
+            dispatch(Actions.setCurrentDateRange('custom'));
+            dispatch(Actions.addTimeSeriesLoading([requestKey]));
+            return getTimeSeries({sites: [site], params: [parmCd], period: period}).then(
+                series => {
+                    const collection = normalize(series, requestKey);
+                    const variables = Object.values(collection.variables);
+                    const variableToDraw = find(variables, v =>  v.variableCode.value === parameterCd);
+                    dispatch(Actions.setCurrentVariable(variableToDraw.variableCode.variableID));
+                    dispatch(Actions.addSeriesCollection(requestKey, collection));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                },
+                () => {
+                    console.log(`Unable to fetch data for period ${period} and parameter code ${parmCd}`);
+                    dispatch(Actions.addSeriesCollection(requestKey, {}));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                }
+            );
+        };
+    },
 
-  // Return the modified store
-  return store;
+    retrieveCustomTimeSeries(site, startTime, endTime, parmCd) {
+        return function(dispatch, getState) {
+            const state = getState();
+            const thisParmCd = parmCd ? parmCd : getCurrentParmCd(state);
+            const requestKey = getTsRequestKey('current', 'custom', thisParmCd)(state);
+
+            dispatch(Actions.setCustomDateRange(startTime, endTime));
+            dispatch(Actions.addTimeSeriesLoading([requestKey]));
+            dispatch(Actions.toggleTimeSeries('median', false));
+            return getTimeSeries({
+                sites: [site],
+                params: [thisParmCd],
+                startDate: startTime,
+                endDate: endTime
+            }).then(
+                series => {
+                    const collection = normalize(series, requestKey);
+                    dispatch(Actions.addSeriesCollection(requestKey, collection));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                },
+                () => {
+                    console.log(`Unable to fetch data for between ${startTime} and ${endTime} and parameter code ${thisParmCd}`);
+                    dispatch(Actions.addSeriesCollection(requestKey, {}));
+                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                }
+            );
+        };
+    },
+    retrieveExtendedTimeSeries(site, period, paramCd=null) {
+        return function(dispatch, getState) {
+            const state = getState();
+            const thisParamCd = paramCd ? paramCd : getCurrentParmCd(state);
+            const requestKey = getTsRequestKey ('current', period, thisParamCd)(state);
+            dispatch(Actions.setCurrentDateRange(period));
+            if (!hasTimeSeries('current', period, thisParamCd)(state)) {
+                dispatch(Actions.addTimeSeriesLoading([requestKey]));
+                const endTime = getRequestTimeRange('current', 'P7D')(state).end;
+                const startTime = calcStartTime(period, endTime);
+                return getTimeSeries({
+                    sites: [site],
+                    params: [thisParamCd],
+                    startDate: startTime,
+                    endDate: endTime
+                }).then(
+                    series => {
+                        const collection = normalize(series, requestKey);
+                        dispatch(Actions.retrieveCompareTimeSeries(site, period, startTime, endTime));
+                        dispatch(Actions.addSeriesCollection(requestKey, collection));
+                        dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                    },
+                    () => {
+                        console.log(`Unable to fetch data for period ${period} and parameter code ${thisParamCd}`);
+                        dispatch(Actions.addSeriesCollection(requestKey, {}));
+                        dispatch(Actions.removeTimeSeriesLoading([requestKey]));
+                    }
+                );
+            } else {
+                return Promise.resolve({});
+            }
+        };
+    },
+    retrieveDailyValueData(monitoringLocationId, timeSeriesId) {
+        return function(dispatch) {
+            return fetchTimeSeries(monitoringLocationId, timeSeriesId)
+                .then(
+                    (data) => {
+                        dispatch(Actions.setObservationsTimeSeries(timeSeriesId, data));
+                    },
+                    () => {
+                        console.log(`Unable to fetch observations time series for ${timeSeriesId}`);
+                    });
+        };
+    },
+    retrieveFloodData(siteno) {
+        return function (dispatch) {
+            const floodFeatures = fetchFloodFeatures(siteno);
+            const floodExtent = fetchFloodExtent(siteno);
+            return Promise.all([floodFeatures, floodExtent]).then((data) => {
+                const [features, extent] = data;
+                const stages = features.map((feature) => feature.attributes.STAGE).sort(function (a, b) {
+                    return a - b;
+                });
+                dispatch(Actions.setFloodFeatures(stages, stages.length ? extent.extent : {}));
+            });
+        };
+    },
+    retrieveNldiData(siteno) {
+        return function (dispatch) {
+            const upstreamFlow = fetchNldiUpstreamFlow(siteno);
+            const downstreamFlow = fetchNldiDownstreamFlow(siteno);
+            const upstreamSites = fetchNldiUpstreamSites(siteno);
+            const downstreamSites = fetchNldiDownstreamSites(siteno);
+            const upstreamBasin = fetchNldiUpstreamBasin(siteno);
+
+            return Promise.all([
+                upstreamFlow, downstreamFlow, upstreamSites, downstreamSites, upstreamBasin
+            ]).then(function(data) {
+               const [upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin] = data;
+               dispatch(Actions.setNldiFeatures(upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin));
+            });
+        };
+    },
+    updateCurrentVariable(siteno, variableID) {
+        return function(dispatch, getState) {
+            dispatch(Actions.setCurrentVariable(variableID));
+            const state = getState();
+            const currentDateRange = getCurrentDateRange(state);
+            if (currentDateRange === 'custom') {
+                const timeRange = getCustomTimeRange(state);
+                dispatch(
+                    Actions.retrieveCustomTimeSeries(siteno, timeRange.startDT, timeRange.endDT));
+            } else {
+                dispatch(Actions.retrieveExtendedTimeSeries(siteno, currentDateRange));
+            }
+        };
+    },
+    startTimeSeriesPlay(maxCursorOffset) {
+        return function (dispatch, getState) {
+            let state = getState().timeSeriesState;
+            if (state.cursorOffset == null || state.cursorOffset >= maxCursorOffset) {
+                dispatch(Actions.setCursorOffset(0));
+            }
+            if (!state.audiblePlayId) {
+                let play = function () {
+                    let newOffset = getState().timeSeriesState.cursorOffset + 15 * 60 * 1000;
+                    if (newOffset > maxCursorOffset) {
+                        dispatch(Actions.stopTimeSeriesPlay());
+                    } else {
+                        dispatch(Actions.setCursorOffset(newOffset));
+                    }
+                };
+                let playId = window.setInterval(play, 10);
+                dispatch(Actions.timeSeriesPlayOn(playId));
+            }
+        };
+    },
+    stopTimeSeriesPlay() {
+        return function(dispatch, getState) {
+            window.clearInterval(getState().timeSeriesState.audiblePlayId);
+            dispatch(Actions.timeSeriesPlayStop());
+        };
+    },
+    timeSeriesPlayOn(playId) {
+        return {
+            type: 'TIME_SERIES_PLAY_ON',
+            playId
+        };
+    },
+    timeSeriesPlayStop() {
+        return {
+            type: 'TIME_SERIES_PLAY_STOP'
+        };
+    },
+    addTimeSeriesLoading(tsKeys) {
+        return {
+            type: 'TIME_SERIES_LOADING_ADD',
+            tsKeys
+        };
+    },
+    removeTimeSeriesLoading(tsKeys) {
+        return {
+            type: 'TIME_SERIES_LOADING_REMOVE',
+            tsKeys
+        };
+    },
+    setFloodFeatures(stages, extent) {
+        return {
+            type: 'SET_FLOOD_FEATURES',
+            stages,
+            extent
+        };
+    },
+    setNldiFeatures(upstreamFlows, downstreamFlows, upstreamSites, downstreamSites, upstreamBasin) {
+        return {
+            type: 'SET_NLDI_FEATURES',
+            upstreamFlows,
+            downstreamFlows,
+            upstreamSites,
+            downstreamSites,
+            upstreamBasin
+        };
+    },
+    setNetworkFeatures(networkSites) {
+         return {
+            type: 'SET_NETWORK_FEATURES',
+            networkSites
+        };
+    },
+    setObservationsTimeSeries(timeSeriesId, data) {
+        return {
+            type: 'SET_OBSERVATIONS_TIME_SERIES',
+            timeSeriesId,
+            data
+        };
+    },
+    toggleTimeSeries(key, show) {
+        return {
+            type: 'TOGGLE_TIME_SERIES',
+            key,
+            show
+        };
+    },
+    addSeriesCollection(key, data) {
+        return {
+            type: 'ADD_TIME_SERIES_COLLECTION',
+            key,
+            data
+        };
+    },
+    resetTimeSeries(key) {
+        return {
+            type: 'RESET_TIME_SERIES',
+            key
+        };
+    },
+    addMedianStats(data) {
+        return {
+            type: 'MEDIAN_STATS_ADD',
+            data
+        };
+    },
+    setCursorOffset(cursorOffset) {
+        return {
+            type: 'SET_CURSOR_OFFSET',
+            cursorOffset
+        };
+    },
+    resizeUI(windowWidth, width) {
+        return {
+            type: 'RESIZE_UI',
+            windowWidth,
+            width
+        };
+    },
+    setHydrographBrushOffset(hydrographBrushOffset) {
+        return {
+            type: 'SET_HYDROGRAPH_BRUSH_OFFSET',
+            hydrographBrushOffset
+        };
+    },
+    clearHydrographBrushOffset() {
+        return {
+            type: 'CLEAR_HYDROGRAPH_BRUSH_OFFSET'
+        };
+    },
+    setCurrentVariable(variableID) {
+        return {
+            type: 'SET_CURRENT_VARIABLE',
+            variableID
+        };
+    },
+    setCurrentMethodID(methodID) {
+        return {
+            type: 'SET_CURRENT_METHOD_ID',
+            methodID
+        };
+    },
+    setCurrentDateRange(period) {
+        return {
+            type: 'SET_CURRENT_DATE_RANGE',
+            period
+        };
+    },
+    setCustomDateRange(startTime, endTime) {
+        return {
+            type: 'SET_CUSTOM_DATE_RANGE',
+            startTime,
+            endTime
+        };
+    },
+    retrieveUserRequestedDataForDateRange(siteno, startTimeStr, endTimeStr) {
+        return function(dispatch, getState) {
+            const state = getState();
+            const locationIanaTimeZone = getIanaTimeZone(state);
+            const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis();
+            const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis();
+            return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime));
+        };
+    },
+    retrieveDataForDateRange(siteno, startTimeStr, endTimeStr, parmCd) {
+        return function(dispatch, getState) {
+            const state = getState();
+            const locationIanaTimeZone = getIanaTimeZone(state);
+            const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis();
+            const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis();
+            return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime, parmCd));
+        };
+    },
+    setGageHeightFromStageIndex(index) {
+        return function(dispatch, getState) {
+            const stages = getState().floodData.stages;
+            if (index > -1 && index < stages.length) {
+                dispatch(Actions.setGageHeight(stages[index]));
+            }
+        };
+    },
+    setGageHeight(gageHeight) {
+        return {
+            type: 'SET_GAGE_HEIGHT',
+            gageHeight
+        };
+    },
+    setLocationIanaTimeZone(ianaTimeZone) {
+        return {
+            type: 'LOCATION_IANA_TIME_ZONE_SET',
+            ianaTimeZone
+        };
+    },
+    setCurrentObservationsTimeSeriesId(timeSeriesId) {
+        return {
+            type: 'SET_CURRENT_TIME_SERIES_ID',
+            timeSeriesId
+        };
+    },
+    /*
+     * @param {Number} cursorOffset - difference in epoch time from the start of the graph to the position of of the cursor
+     */
+    setDailyValueCursorOffset(cursorOffset) {
+        return {
+            type: 'SET_DAILY_VALUE_CURSOR_OFFSET',
+            cursorOffset
+        };
+    }
 };
 
+const appReducer = combineReducers({
+    series,
+    observationsData,
+    statisticsData,
+    floodData,
+    nldiData,
+    timeSeriesState,
+    observationsState,
+    floodState,
+    ui
+});
+
+const MIDDLEWARES = [thunk];
+
+
+export const configureStore = function (initialState) {
+    initialState = {
+        series: {},
+        observationsData: {},
+        floodData: {
+            stages: [],
+            extent: {}
+        },
+        nldiData: {
+            upstreamFlows: [],
+            downstreamFlows: [],
+            upstreamSites: [],
+            downstreamSites: [],
+            upstreamBasin: []
+        },
+        statisticsData: {},
+        timeSeriesState: {
+            showSeries: {
+                current: true,
+                compare: false,
+                median: false
+            },
+            currentDateRange: 'P7D',
+            customTimeRange: null,
+            currentVariableID: null,
+            cursorOffset: null,
+            audiblePlayId: null,
+            loadingTSKeys: [],
+            hydrographBrushOffset: null
+        },
+        observationsState: {
+            cursorOffset: null
+        },
+        floodState: {
+            gageHeight: null
+        },
+        ui : {
+            windowWidth: 1024,
+            width: 800
+        },
+        ...initialState
+    };
+
+    let enhancers;
+    if (window.__REDUX_DEVTOOLS_EXTENSION__) {
+        enhancers = compose(
+            applyMiddleware(...MIDDLEWARES),
+            window.__REDUX_DEVTOOLS_EXTENSION__({serialize: true})
+        );
+    } else {
+        enhancers = applyMiddleware(...MIDDLEWARES);
+    }
+
+    return createStore(
+        appReducer,
+        initialState,
+        enhancers
+    );
+};
\ No newline at end of file
diff --git a/assets/src/scripts/store/site-store.spec.js b/assets/src/scripts/store/index.spec.js
similarity index 99%
rename from assets/src/scripts/store/site-store.spec.js
rename to assets/src/scripts/store/index.spec.js
index 49610eaa2..583feabf1 100644
--- a/assets/src/scripts/store/site-store.spec.js
+++ b/assets/src/scripts/store/index.spec.js
@@ -1,5 +1,5 @@
-import { Actions, configureStore } from './site-store';
-import { MOCK_RDB as MOCK_STATS_DATA } from '../web-services/statistics-data.spec.js';
+import {Actions, configureStore} from './index';
+import {MOCK_RDB as MOCK_STATS_DATA} from '../web-services/statistics-data.spec.js';
 
 describe('Redux store', () => {
 
diff --git a/assets/src/scripts/store/nldi-data-reducer.spec.js b/assets/src/scripts/store/nldi-data-reducer.spec.js
index 77e7763f8..e2e7af7d2 100644
--- a/assets/src/scripts/store/nldi-data-reducer.spec.js
+++ b/assets/src/scripts/store/nldi-data-reducer.spec.js
@@ -1,4 +1,4 @@
-import { nldiDataReducer } from './nldi-data-reducer';
+import {nldiDataReducer} from './nldi-data-reducer';
 
 describe('nldi-data-reducer', () => {
 
diff --git a/assets/src/scripts/store/series-reducer.spec.js b/assets/src/scripts/store/series-reducer.spec.js
index b36c95e7c..351800fee 100644
--- a/assets/src/scripts/store/series-reducer.spec.js
+++ b/assets/src/scripts/store/series-reducer.spec.js
@@ -1,5 +1,5 @@
 
-import { seriesReducer } from './series-reducer';
+import {seriesReducer} from './series-reducer';
 
 describe('series-reducer', () => {
 
diff --git a/assets/src/scripts/store/site-store.js b/assets/src/scripts/store/site-store.js
deleted file mode 100644
index 9770760c0..000000000
--- a/assets/src/scripts/store/site-store.js
+++ /dev/null
@@ -1,560 +0,0 @@
-
-import find from 'lodash/find';
-import findKey from 'lodash/findKey';
-import last from 'lodash/last';
-import {DateTime} from 'luxon';
-
-import {normalize} from '../schema';
-import {calcStartTime, sortedParameters} from '../utils';
-
-import {getCurrentParmCd, getCurrentDateRange, hasTimeSeries, getTsRequestKey, getRequestTimeRange,
-    getCustomTimeRange, getIanaTimeZone} from '../selectors/time-series-selector';
-
-import {fetchFloodFeatures, fetchFloodExtent} from '../web-services/flood-data';
-import {getPreviousYearTimeSeries, getTimeSeries, queryWeatherService} from '../web-services/models';
-import {fetchNldiUpstreamSites, fetchNldiDownstreamSites, fetchNldiDownstreamFlow, fetchNldiUpstreamFlow, fetchNldiUpstreamBasin} from '../web-services/nldi-data';
-import {fetchTimeSeries} from '../web-services/observations';
-import {fetchSiteStatistics} from '../web-services/statistics-data';
-import {fetchNetworkSites} from '../networks/network-data';
-
-import {floodDataReducer as floodData} from './flood-data-reducer';
-import {floodStateReducer as floodState} from './flood-state-reducer';
-import {nldiDataReducer as nldiData} from './nldi-data-reducer';
-import {observationsDataReducer as observationsData} from './observations-data-reducer';
-import {observationsStateReducer as observationsState} from './observations-state-reducer';
-import {seriesReducer as series} from './series-reducer';
-import {statisticsDataReducer as statisticsData} from './statistics-data-reducer';
-import {timeSeriesStateReducer as timeSeriesState} from './time-series-state-reducer';
-import {uiReducer as ui} from './ui-reducer';
-import {configureReduxStore} from './index';
-
-const GAGE_HEIGHT_CD = '00065';
-/*
- * Helper functions
- */
-const getLatestValue = function(collection, parmCd) {
-    let parmVar = findKey(collection.variables, (varValue) => {
-        return varValue.variableCode.value === parmCd;
-    });
-    let parmTimeSeries = findKey(collection.timeSeries, (ts) => {
-        return ts.variable === parmVar;
-    });
-    let points = parmTimeSeries ? collection.timeSeries[parmTimeSeries].points : [];
-    return points.length ? last(points).value : null;
-};
-
-
-/*
- * @param {Object} timeSeries - keys are time series id
- * @param {Object} variables  - keys are the variable id
- */
-const getCurrentVariableId = function(timeSeries, variables) {
-    const tsVariablesWithData = Object.values(timeSeries)
-        .filter((ts) => ts.points.length)
-        .map((ts) => variables[ts.variable]);
-    const sortedVarsWithData = sortedParameters(tsVariablesWithData);
-    if (sortedVarsWithData.length) {
-        return sortedVarsWithData[0].oid;
-    } else {
-        const sortedVars = sortedParameters(Object.values(variables));
-        return sortedVars.length ? sortedVars[0].oid : '';
-    }
-};
-
-export const Actions = {
-    retrieveLocationTimeZone(latitude, longitude) {
-        return function(dispatch) {
-            return queryWeatherService(latitude, longitude).then(
-                resp => {
-                    const tzIANA = resp.properties.timeZone || null; // set to time zone to null if unavailable
-                    dispatch(Actions.setLocationIanaTimeZone(tzIANA));
-                },
-                () => {
-                    dispatch(Actions.setLocationIanaTimeZone(null));
-                }
-            );
-        };
-    },
-    retrieveTimeSeries(siteno, params=null) {
-        return function (dispatch, getState) {
-            const currentState = getState();
-            const requestKey = getTsRequestKey('current', 'P7D')(currentState);
-            dispatch(Actions.addTimeSeriesLoading([requestKey]));
-            return getTimeSeries({sites: [siteno], params}).then(
-                series => {
-                    const collection = normalize(series, requestKey);
-
-                    // Get the start/end times of this request's range.
-                    const notes = collection.queryInfo[requestKey].notes;
-                    const endTime = notes.requestDT;
-                    const startTime = calcStartTime('P7D', endTime, 'local');
-
-                    // Trigger a call to get last year's data
-                    dispatch(Actions.retrieveCompareTimeSeries(siteno, 'P7D', startTime, endTime));
-
-                    // Update the series data for the 'current' series
-                    dispatch(Actions.addSeriesCollection('current', collection));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-
-                    // Update the application state
-                    dispatch(Actions.toggleTimeSeries('current', true));
-                    const variable = getCurrentVariableId(collection.timeSeries || {}, collection.variables || {});
-                    dispatch(Actions.setCurrentVariable(variable));
-                    dispatch(Actions.setGageHeight(getLatestValue(collection, GAGE_HEIGHT_CD)));
-                },
-                () => {
-                    dispatch(Actions.resetTimeSeries(getTsRequestKey('current', 'P7D')(currentState)));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-
-                    dispatch(Actions.toggleTimeSeries('current', false));
-                }
-            );
-        };
-    },
-    retrieveCompareTimeSeries(site, period, startTime, endTime) {
-        return function (dispatch, getState) {
-            const requestKey = getTsRequestKey('compare', period)(getState());
-            dispatch(Actions.addTimeSeriesLoading([requestKey]));
-            return getPreviousYearTimeSeries({site, startTime, endTime}).then(
-                series => {
-                    const collection = normalize(series, requestKey);
-                    dispatch(Actions.addSeriesCollection(requestKey, collection));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                },
-                () => {
-                    dispatch(Actions.resetTimeSeries(getTsRequestKey('compare', period)(getState())));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                }
-            );
-        };
-    },
-    retrieveMedianStatistics(site) {
-        return function(dispatch) {
-            return fetchSiteStatistics({site, statType: 'median'}).then(
-                stats => {
-                    dispatch(Actions.addMedianStats(stats));
-                }
-            );
-        };
-    },
-
-    retrieveCustomTimePeriodTimeSeries(site, parameterCd, period) {
-        return function(dispatch, getState) {
-            const state = getState();
-            const parmCd = parameterCd;
-            const requestKey = getTsRequestKey('current', 'custom', parmCd)(state);
-            dispatch(Actions.setCurrentDateRange('custom'));
-            dispatch(Actions.addTimeSeriesLoading([requestKey]));
-            return getTimeSeries({sites: [site], params: [parmCd], period: period}).then(
-                series => {
-                    const collection = normalize(series, requestKey);
-                    const variables = Object.values(collection.variables);
-                    const variableToDraw = find(variables, v =>  v.variableCode.value === parameterCd);
-                    dispatch(Actions.setCurrentVariable(variableToDraw.variableCode.variableID));
-                    dispatch(Actions.addSeriesCollection(requestKey, collection));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                },
-                () => {
-                    console.log(`Unable to fetch data for period ${period} and parameter code ${parmCd}`);
-                    dispatch(Actions.addSeriesCollection(requestKey, {}));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                }
-            );
-        };
-    },
-
-    retrieveCustomTimeSeries(site, startTime, endTime, parmCd) {
-        return function(dispatch, getState) {
-            const state = getState();
-            const thisParmCd = parmCd ? parmCd : getCurrentParmCd(state);
-            const requestKey = getTsRequestKey('current', 'custom', thisParmCd)(state);
-
-            dispatch(Actions.setCustomDateRange(startTime, endTime));
-            dispatch(Actions.addTimeSeriesLoading([requestKey]));
-            dispatch(Actions.toggleTimeSeries('median', false));
-            return getTimeSeries({
-                sites: [site],
-                params: [thisParmCd],
-                startDate: startTime,
-                endDate: endTime
-            }).then(
-                series => {
-                    const collection = normalize(series, requestKey);
-                    dispatch(Actions.addSeriesCollection(requestKey, collection));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                },
-                () => {
-                    console.log(`Unable to fetch data for between ${startTime} and ${endTime} and parameter code ${thisParmCd}`);
-                    dispatch(Actions.addSeriesCollection(requestKey, {}));
-                    dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                }
-            );
-        };
-    },
-    retrieveExtendedTimeSeries(site, period, paramCd=null) {
-        return function(dispatch, getState) {
-            const state = getState();
-            const thisParamCd = paramCd ? paramCd : getCurrentParmCd(state);
-            const requestKey = getTsRequestKey ('current', period, thisParamCd)(state);
-            dispatch(Actions.setCurrentDateRange(period));
-            if (!hasTimeSeries('current', period, thisParamCd)(state)) {
-                dispatch(Actions.addTimeSeriesLoading([requestKey]));
-                const endTime = getRequestTimeRange('current', 'P7D')(state).end;
-                const startTime = calcStartTime(period, endTime);
-                return getTimeSeries({
-                    sites: [site],
-                    params: [thisParamCd],
-                    startDate: startTime,
-                    endDate: endTime
-                }).then(
-                    series => {
-                        const collection = normalize(series, requestKey);
-                        dispatch(Actions.retrieveCompareTimeSeries(site, period, startTime, endTime));
-                        dispatch(Actions.addSeriesCollection(requestKey, collection));
-                        dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                    },
-                    () => {
-                        console.log(`Unable to fetch data for period ${period} and parameter code ${thisParamCd}`);
-                        dispatch(Actions.addSeriesCollection(requestKey, {}));
-                        dispatch(Actions.removeTimeSeriesLoading([requestKey]));
-                    }
-                );
-            } else {
-                return Promise.resolve({});
-            }
-        };
-    },
-    retrieveDailyValueData(monitoringLocationId, timeSeriesId) {
-        return function(dispatch) {
-            return fetchTimeSeries(monitoringLocationId, timeSeriesId)
-                .then(
-                    (data) => {
-                        dispatch(Actions.setObservationsTimeSeries(timeSeriesId, data));
-                    },
-                    () => {
-                        console.log(`Unable to fetch observations time series for ${timeSeriesId}`);
-                    });
-        };
-    },
-    retrieveFloodData(siteno) {
-        return function (dispatch) {
-            const floodFeatures = fetchFloodFeatures(siteno);
-            const floodExtent = fetchFloodExtent(siteno);
-            return Promise.all([floodFeatures, floodExtent]).then((data) => {
-                const [features, extent] = data;
-                const stages = features.map((feature) => feature.attributes.STAGE).sort(function (a, b) {
-                    return a - b;
-                });
-                dispatch(Actions.setFloodFeatures(stages, stages.length ? extent.extent : {}));
-            });
-        };
-    },
-    retrieveNldiData(siteno) {
-        return function (dispatch) {
-            const upstreamFlow = fetchNldiUpstreamFlow(siteno);
-            const downstreamFlow = fetchNldiDownstreamFlow(siteno);
-            const upstreamSites = fetchNldiUpstreamSites(siteno);
-            const downstreamSites = fetchNldiDownstreamSites(siteno);
-            const upstreamBasin = fetchNldiUpstreamBasin(siteno);
-
-            return Promise.all([
-                upstreamFlow, downstreamFlow, upstreamSites, downstreamSites, upstreamBasin
-            ]).then(function(data) {
-               const [upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin] = data;
-               dispatch(Actions.setNldiFeatures(upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin));
-            });
-        };
-    },
-    retrieveNetworkData(networkCd) {
-        return function(dispatch) {
-            const networkSites = fetchNetworkSites(networkCd);
-
-            return Promise.all( [networkSites]
-            ).then(function(data){
-                const [networkSites] = data;
-                dispatch(Actions.setNetworkFeatures(networkSites));
-            });
-        };
-    },
-    updateCurrentVariable(siteno, variableID) {
-        return function(dispatch, getState) {
-            dispatch(Actions.setCurrentVariable(variableID));
-            const state = getState();
-            const currentDateRange = getCurrentDateRange(state);
-            if (currentDateRange === 'custom') {
-                const timeRange = getCustomTimeRange(state);
-                dispatch(
-                    Actions.retrieveCustomTimeSeries(siteno, timeRange.startDT, timeRange.endDT));
-            } else {
-                dispatch(Actions.retrieveExtendedTimeSeries(siteno, currentDateRange));
-            }
-        };
-    },
-    startTimeSeriesPlay(maxCursorOffset) {
-        return function (dispatch, getState) {
-            let state = getState().timeSeriesState;
-            if (state.cursorOffset == null || state.cursorOffset >= maxCursorOffset) {
-                dispatch(Actions.setCursorOffset(0));
-            }
-            if (!state.audiblePlayId) {
-                let play = function () {
-                    let newOffset = getState().timeSeriesState.cursorOffset + 15 * 60 * 1000;
-                    if (newOffset > maxCursorOffset) {
-                        dispatch(Actions.stopTimeSeriesPlay());
-                    } else {
-                        dispatch(Actions.setCursorOffset(newOffset));
-                    }
-                };
-                let playId = window.setInterval(play, 10);
-                dispatch(Actions.timeSeriesPlayOn(playId));
-            }
-        };
-    },
-    stopTimeSeriesPlay() {
-        return function(dispatch, getState) {
-            window.clearInterval(getState().timeSeriesState.audiblePlayId);
-            dispatch(Actions.timeSeriesPlayStop());
-        };
-    },
-    timeSeriesPlayOn(playId) {
-        return {
-            type: 'TIME_SERIES_PLAY_ON',
-            playId
-        };
-    },
-    timeSeriesPlayStop() {
-        return {
-            type: 'TIME_SERIES_PLAY_STOP'
-        };
-    },
-    addTimeSeriesLoading(tsKeys) {
-        return {
-            type: 'TIME_SERIES_LOADING_ADD',
-            tsKeys
-        };
-    },
-    removeTimeSeriesLoading(tsKeys) {
-        return {
-            type: 'TIME_SERIES_LOADING_REMOVE',
-            tsKeys
-        };
-    },
-    setFloodFeatures(stages, extent) {
-        return {
-            type: 'SET_FLOOD_FEATURES',
-            stages,
-            extent
-        };
-    },
-    setNldiFeatures(upstreamFlows, downstreamFlows, upstreamSites, downstreamSites, upstreamBasin) {
-        return {
-            type: 'SET_NLDI_FEATURES',
-            upstreamFlows,
-            downstreamFlows,
-            upstreamSites,
-            downstreamSites,
-            upstreamBasin
-        };
-    },
-    setNetworkFeatures(networkSites) {
-         return {
-            type: 'SET_NETWORK_FEATURES',
-            networkSites
-        };
-    },
-    setObservationsTimeSeries(timeSeriesId, data) {
-        return {
-            type: 'SET_OBSERVATIONS_TIME_SERIES',
-            timeSeriesId,
-            data
-        };
-    },
-    toggleTimeSeries(key, show) {
-        return {
-            type: 'TOGGLE_TIME_SERIES',
-            key,
-            show
-        };
-    },
-    addSeriesCollection(key, data) {
-        return {
-            type: 'ADD_TIME_SERIES_COLLECTION',
-            key,
-            data
-        };
-    },
-    resetTimeSeries(key) {
-        return {
-            type: 'RESET_TIME_SERIES',
-            key
-        };
-    },
-    addMedianStats(data) {
-        return {
-            type: 'MEDIAN_STATS_ADD',
-            data
-        };
-    },
-    setCursorOffset(cursorOffset) {
-        return {
-            type: 'SET_CURSOR_OFFSET',
-            cursorOffset
-        };
-    },
-    resizeUI(windowWidth, width) {
-        return {
-            type: 'RESIZE_UI',
-            windowWidth,
-            width
-        };
-    },
-    setHydrographBrushOffset(hydrographBrushOffset) {
-        return {
-            type: 'SET_HYDROGRAPH_BRUSH_OFFSET',
-            hydrographBrushOffset
-        };
-    },
-    clearHydrographBrushOffset() {
-        return {
-            type: 'CLEAR_HYDROGRAPH_BRUSH_OFFSET'
-        };
-    },
-    setCurrentVariable(variableID) {
-        return {
-            type: 'SET_CURRENT_VARIABLE',
-            variableID
-        };
-    },
-    setCurrentMethodID(methodID) {
-        return {
-            type: 'SET_CURRENT_METHOD_ID',
-            methodID
-        };
-    },
-    setCurrentDateRange(period) {
-        return {
-            type: 'SET_CURRENT_DATE_RANGE',
-            period
-        };
-    },
-    setCustomDateRange(startTime, endTime) {
-        return {
-            type: 'SET_CUSTOM_DATE_RANGE',
-            startTime,
-            endTime
-        };
-    },
-    retrieveUserRequestedDataForDateRange(siteno, startTimeStr, endTimeStr) {
-        return function(dispatch, getState) {
-            const state = getState();
-            const locationIanaTimeZone = getIanaTimeZone(state);
-            const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis();
-            const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis();
-            return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime));
-        };
-    },
-    retrieveDataForDateRange(siteno, startTimeStr, endTimeStr, parmCd) {
-        return function(dispatch, getState) {
-            const state = getState();
-            const locationIanaTimeZone = getIanaTimeZone(state);
-            const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis();
-            const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis();
-            return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime, parmCd));
-        };
-    },
-    setGageHeightFromStageIndex(index) {
-        return function(dispatch, getState) {
-            const stages = getState().floodData.stages;
-            if (index > -1 && index < stages.length) {
-                dispatch(Actions.setGageHeight(stages[index]));
-            }
-        };
-    },
-    setGageHeight(gageHeight) {
-        return {
-            type: 'SET_GAGE_HEIGHT',
-            gageHeight
-        };
-    },
-    setLocationIanaTimeZone(ianaTimeZone) {
-        return {
-            type: 'LOCATION_IANA_TIME_ZONE_SET',
-            ianaTimeZone
-        };
-    },
-    setCurrentObservationsTimeSeriesId(timeSeriesId) {
-        return {
-            type: 'SET_CURRENT_TIME_SERIES_ID',
-            timeSeriesId
-        };
-    },
-    /*
-     * @param {Number} cursorOffset - difference in epoch time from the start of the graph to the position of of the cursor
-     */
-    setDailyValueCursorOffset(cursorOffset) {
-        return {
-            type: 'SET_DAILY_VALUE_CURSOR_OFFSET',
-            cursorOffset
-        };
-    }
-};
-
-const reducers = {
-    series,
-    observationsData,
-    statisticsData,
-    floodData,
-    nldiData,
-    timeSeriesState,
-    observationsState,
-    floodState,
-    ui
-};
-
-export const configureStore = function(initialState){
-
-     initialState = {
-         series: {},
-         observationsData: {},
-         floodData: {
-             stages: [],
-             extent: {}
-         },
-         nldiData: {
-             upstreamFlows: [],
-             downstreamFlows: [],
-             upstreamSites: [],
-             downstreamSites: [],
-             upstreamBasin: []
-         },
-         statisticsData: {},
-
-         timeSeriesState: {
-             showSeries: {
-                 current: true,
-                 compare: false,
-                 median: false
-             },
-             currentDateRange: 'P7D',
-             customTimeRange: null,
-             currentVariableID: null,
-             cursorOffset: null,
-             audiblePlayId: null,
-             loadingTSKeys: [],
-             hydrographBrushOffset: null
-         },
-         observationsState: {
-             cursorOffset: null
-         },
-         floodState: {
-             gageHeight: null
-         },
-         ui: {
-             windowWidth: 1024,
-             width: 800
-         },
-         ...initialState
-     };
-
-    return configureReduxStore(initialState, reducers);
-};
diff --git a/assets/src/scripts/store/time-series-state-reducer.spec.js b/assets/src/scripts/store/time-series-state-reducer.spec.js
index 35171e552..b10c6e3e6 100644
--- a/assets/src/scripts/store/time-series-state-reducer.spec.js
+++ b/assets/src/scripts/store/time-series-state-reducer.spec.js
@@ -1,5 +1,5 @@
 
-import { timeSeriesStateReducer } from './time-series-state-reducer';
+import {timeSeriesStateReducer} from './time-series-state-reducer';
 
 describe('time-series-state-reducer', () => {
 
diff --git a/assets/src/scripts/store/ui-reducer.spec.js b/assets/src/scripts/store/ui-reducer.spec.js
index 8d583bf4c..1791d167e 100644
--- a/assets/src/scripts/store/ui-reducer.spec.js
+++ b/assets/src/scripts/store/ui-reducer.spec.js
@@ -1,4 +1,4 @@
-import { uiReducer } from './ui-reducer';
+import {uiReducer} from './ui-reducer';
 
 describe('uiReducer', () => {
 
diff --git a/assets/src/scripts/tooltips.spec.js b/assets/src/scripts/tooltips.spec.js
index 781513f08..7eea83dd0 100644
--- a/assets/src/scripts/tooltips.spec.js
+++ b/assets/src/scripts/tooltips.spec.js
@@ -1,5 +1,5 @@
-import { select } from 'd3-selection';
-import { appendTooltip } from './tooltips';
+import {select} from 'd3-selection';
+import {appendTooltip} from './tooltips';
 
 
 describe('tooltips', () => {
diff --git a/assets/src/scripts/url-params.spec.js b/assets/src/scripts/url-params.spec.js
index 464ab5a7d..a0b6994c0 100644
--- a/assets/src/scripts/url-params.spec.js
+++ b/assets/src/scripts/url-params.spec.js
@@ -1,5 +1,4 @@
-
-import {configureStore, Actions} from './store/site-store';
+import {configureStore, Actions} from './store';
 import {getParamString, renderTimeSeriesUrlParams} from './url-params';
 
 describe('url-params module', () => {
diff --git a/assets/src/scripts/web-services/flood-data.js b/assets/src/scripts/web-services/flood-data.js
index de4e0e842..1b2ee6a6d 100644
--- a/assets/src/scripts/web-services/flood-data.js
+++ b/assets/src/scripts/web-services/flood-data.js
@@ -1,4 +1,4 @@
-import { get } from '../ajax';
+import {get} from '../ajax';
 import config from '../config';
 
 
diff --git a/assets/src/scripts/web-services/flood-data.spec.js b/assets/src/scripts/web-services/flood-data.spec.js
index 0425e2956..0ced88d1b 100644
--- a/assets/src/scripts/web-services/flood-data.spec.js
+++ b/assets/src/scripts/web-services/flood-data.spec.js
@@ -1,4 +1,4 @@
-import { fetchFloodExtent, fetchFloodFeatures } from './flood-data';
+import {fetchFloodExtent, fetchFloodFeatures} from './flood-data';
 
 
 describe('flood_data module', () => {
diff --git a/assets/src/scripts/web-services/nldi-data.js b/assets/src/scripts/web-services/nldi-data.js
index 0da30ee7c..107716d83 100644
--- a/assets/src/scripts/web-services/nldi-data.js
+++ b/assets/src/scripts/web-services/nldi-data.js
@@ -1,5 +1,5 @@
 import config from '../config';
-import { get } from '../ajax';
+import {get} from '../ajax';
 
 const nldiUrl = config.NLDI_SERVICES_ENDPOINT;
 const featureSource = 'nwissite';
diff --git a/assets/src/scripts/web-services/nldi-data.spec.js b/assets/src/scripts/web-services/nldi-data.spec.js
index f26de85ce..4b244d0b5 100644
--- a/assets/src/scripts/web-services/nldi-data.spec.js
+++ b/assets/src/scripts/web-services/nldi-data.spec.js
@@ -1,4 +1,4 @@
-import { fetchNldiUpstreamSites, fetchNldiUpstreamFlow, fetchNldiDownstreamSites, fetchNldiDownstreamFlow,
+import {fetchNldiUpstreamSites, fetchNldiUpstreamFlow, fetchNldiDownstreamSites, fetchNldiDownstreamFlow,
          fetchNldiUpstreamBasin} from './nldi-data';
 
 describe('nldi-data module', () => {
diff --git a/assets/src/scripts/web-services/observations.js b/assets/src/scripts/web-services/observations.js
index 5d4358055..30435093e 100644
--- a/assets/src/scripts/web-services/observations.js
+++ b/assets/src/scripts/web-services/observations.js
@@ -1,5 +1,5 @@
 
-import { get } from '../ajax';
+import {get} from '../ajax';
 import config from '../config';
 
 import { DV_DATA } from '../dv_414240072033201';
diff --git a/assets/src/scripts/web-services/statistics-data.js b/assets/src/scripts/web-services/statistics-data.js
index fe4fa3e90..d137d36a5 100644
--- a/assets/src/scripts/web-services/statistics-data.js
+++ b/assets/src/scripts/web-services/statistics-data.js
@@ -1,4 +1,4 @@
-import { get } from '../ajax';
+import {get} from '../ajax';
 import config from '../config';
 import { parseRDB } from '../utils';
 
diff --git a/assets/src/scripts/web-services/statistics-data.spec.js b/assets/src/scripts/web-services/statistics-data.spec.js
index 7e075b19b..fa47f00dd 100644
--- a/assets/src/scripts/web-services/statistics-data.spec.js
+++ b/assets/src/scripts/web-services/statistics-data.spec.js
@@ -1,4 +1,4 @@
-import { fetchSiteStatistics, fetchSitesStatisticsRDB } from './statistics-data';
+import {fetchSiteStatistics, fetchSitesStatisticsRDB} from './statistics-data';
 
 
 describe('statistics-data', () => {
diff --git a/assets/src/styles/components/_network-map.scss b/assets/src/styles/components/_network.scss
similarity index 100%
rename from assets/src/styles/components/_network-map.scss
rename to assets/src/styles/components/_network.scss
diff --git a/assets/src/styles/less/vendor.less b/assets/src/styles/less/vendor.less
deleted file mode 100644
index 37d517f23..000000000
--- a/assets/src/styles/less/vendor.less
+++ /dev/null
@@ -1,4 +0,0 @@
-@import (inline) '../../../node_modules/leaflet/dist/leaflet.css';
-@import (inline) '../../../node_modules/leaflet.markercluster/dist/MarkerCluster.css';
-@import (inline) '../../../node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css';
-@import (inline) '../../../node_modules/tabulator-tables/dist/css/tabulator.css';
diff --git a/assets/src/styles/network.scss b/assets/src/styles/network.scss
index 323b894d9..f565f6d6c 100644
--- a/assets/src/styles/network.scss
+++ b/assets/src/styles/network.scss
@@ -4,6 +4,9 @@ $fa-font-path: './fonts' !default;
 @import '../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
 @import '../../node_modules/@fortawesome/fontawesome-free/scss/brands';
 @import '../../node_modules/@fortawesome/fontawesome-free/scss/solid';
+@import '../../node_modules/leaflet/dist/leaflet';
+@import '../../node_modules/leaflet.markercluster/dist/MarkerCluster';
+@import '../../node_modules/leaflet.markercluster/dist/MarkerCluster.Default';
 
 @import 'partials/script';
 @import 'partials/slider';
@@ -11,8 +14,8 @@ $fa-font-path: './fonts' !default;
 @import 'partials/tooltip';
 
 .wdfn-component {
-  &[data-component='network-map'] {
-    @import './components/network-map';
+  &[data-component='network'] {
+    @import './components/network';
     margin-bottom: 1em;
   }
 }
@@ -70,30 +73,110 @@ body {
   }
 }
 
-.site_overload {
-  display: none;
+sort {
+  text-align: left;
+  border: none;
+  background: none;
+  display: block;
+  width: 100%;
+  color: #808080;
+  line-height: 14px;
+}
+.sort.textcenter { text-align:center; }
+.sort.textright { text-align:right; }
+
+.caret { line-height:0; }
+.caret:after { content:""; }
+.asc .caret:after {
+  width: 0;
+  height: 0;
+  border-left: 10px solid transparent;
+  border-right: 10px solid transparent;
+  border-top: 10px solid #808080;
+  content: "";
+  position: relative;
+  top: 0px;
+  right: -10px;
+  font-size: 0;
+}
+.desc .caret:after {
+  width: 0;
+  height: 0;
+  border-left: 10px solid transparent;
+  border-right: 10px solid transparent;
+  border-bottom: 10px solid #808080;
+  content: "";
+  position: relative;
+  top: -10px;
+  right: -10px;
+  font-size: 0;
+}
+
+.sort .caret { display:none; }
+.sort.asc .caret { display:inline-block; }
+.sort.desc .caret { display:inline-block; }
+
+button.sort {
+  background: none;
+  background-color: transparent;
+  box-shadow: none;
+  border: none;
+  font-family: inherit;
+  font-weight: bold;
+  color: inherit;
+  border-radius: 0;
+  cursor: pointer;
+  width: 100%;
+  text-align: left;
+  outline: none;
+}
+
+.table-pagination {
+  display: table;
+  text-align: center;
+  border:1px solid #808080;
+  float: left;
+  padding: 6px;
+}
+
+.btn-tablepage {
+  display: table-cell;
+  width: 33px;
+  padding: 9px;
 }
 
 .pagination {
-  li {
-    display: inline-block;
-    margin-left: 0.6rem;
-    padding: .8rem;
-    @include u-border('solid');
-    @include u-border(1px);
-    @include u-radius('md');
-    a {
-      text-decoration: none;
-    }
-  }
-  .active {
-    background-color: color('gray-20');
-  }
+  display: table-cell;
+  text-align: center;
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+.pagination li {
+  display: inline-table;
+  border: 1px solid #eaeaea;
+}
 
-  .disabled {
-    border: none;
-    a {
-      cursor: not-allowed;
-    }
-  }
-}
\ No newline at end of file
+.page {
+  display: block;
+  padding: 4px 0;
+  width: 38px;
+  text-align: center;
+  text-decoration: none;
+  line-height: 25px;
+  font-size: 18px;
+  color: #808080;
+  background-color: white;
+}
+
+#search-name{
+  float: left;
+  margin-right: 20px;
+}
+
+.pagination .active { border-color:#aaa; }
+.active .page {  background:#aaa; color:#fff; }
+
+#link-table{
+  width: 100%;
+}
diff --git a/assets/tests/scripts/networks/karma.conf.js b/assets/tests/scripts/networks/karma.conf.js
new file mode 100644
index 000000000..6bf4baa90
--- /dev/null
+++ b/assets/tests/scripts/networks/karma.conf.js
@@ -0,0 +1,360 @@
+var istanbul = require('rollup-plugin-istanbul');
+
+
+function isDebug(argument) {
+    return argument === '--debug';
+}
+
+/**
+ * Karma configuration for WDFN assets
+ */
+
+module.exports = function (config) {
+    /**
+     * Base configuration shared by all run configurations
+     */
+    let karmaConfig = {
+        // base path that will be used to resolve all patterns (eg. files, exclude)
+        basePath: '',
+
+        // frameworks to use
+        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
+        frameworks: ['jasmine-ajax', 'jasmine'],
+
+        client: {
+          jasmine: {
+            random: false
+          }
+        },
+
+        // list of files / patterns to load in the browser
+        files: [
+            '../global-config.js',
+            '../../../node_modules/leaflet/dist/leaflet.js',
+            '../../../node_modules/esri-leaflet/dist/esri-leaflet.js',
+            '../../../node_modules/leaflet.markercluster/dist/leaflet.markercluster.js',
+            {pattern: '../../../src/scripts/networks/index.spec.js', watched: false}
+
+        ],
+
+        // list of files / patterns to exclude
+        exclude: [
+            '../../../src/scripts/networks/index.js'
+        ],
+
+        // preprocess matching files before serving them to the browser
+        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
+        preprocessors: {
+            '../../../src/scripts/networks/index.spec.js': ['rollup']
+        },
+
+        rollupPreprocessor: {
+            /**
+             * This is just a normal Rollup config object,
+             * except that `input` is handled for you.
+             */
+            ...require('../../../rollup.config')[1]
+        },
+
+        // test results reporter to use
+        // possible values: 'dots', 'progress'
+        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
+        reporters: ['spec'],
+
+        // web server port
+        port: 9876,
+
+        // enable / disable colors in the output (reporters and logs)
+        colors: true,
+
+        // level of logging
+        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+        logLevel: config.LOG_INFO,
+
+        // enable / disable watching file and executing tests whenever any file changes
+        autoWatch: false,
+
+        browsers: ['FirefoxHeadless'],
+        customLaunchers: getCustomLaunchers(),  /* eslint no-use-before-define: 0 */
+
+
+        // Continuous Integration mode
+        // if true, Karma captures browsers, runs the tests and exits
+        singleRun: true,
+
+        // Concurrency level
+        // how many browser should be started simultaneous
+        concurrency: Infinity
+    };
+
+    /**
+     * Produce a code coverage report
+     */
+    if (!process.env.KARMA_SAUCE_LABS && !process.argv.some(isDebug)) {
+        karmaConfig = {
+            ...karmaConfig,
+            rollupPreprocessor: {
+                ...karmaConfig.rollupPreprocessor,
+                plugins: [
+                    ...karmaConfig.rollupPreprocessor.plugins,
+                    istanbul({
+                        include: [
+                            '../../../src/scripts/networks/*.js'
+                        ],
+                        exclude: [
+                            '../../../src/scripts/networks/*.spec.js'
+                        ]
+                    })
+                ]
+            },
+            reporters: [
+                ...karmaConfig.reporters,
+                'coverage'
+            ],
+            coverageReporter: {
+                reporters: [
+                    //{type: 'html', dir: 'coverage/'},
+                    {type: 'cobertura', dir: 'coverage/'},
+                    {type: 'lcovonly', dir: 'coverage/'}
+                ]
+            }
+        };
+    } else {
+        console.log('Skipping code coverage report...');
+    }
+
+
+
+    /**
+     * Add BrowserStack-specific settings
+     */
+    if (process.env.KARMA_BROWSERSTACK && process.env.BROWSER_STACK_USERNAME) {
+        console.log('Using BrowserStack configuration...');
+        karmaConfig = {
+            ...karmaConfig,
+            browserStack: {
+                project: 'Water Data For The Nation',
+                timeout: 1800
+            },
+            browserNoActivityTimeout: 120000,
+            concurrency: 2,
+            // These default browsers are chosen to prevent loss of browser
+            // compatibility. As of May 15, 2018 they are the current oldest
+            // supported.
+            browsers: [
+                ...karmaConfig.browsers,
+
+                'bs_safarilatest_mac',
+                // iOS Safari no longer working from VMs
+                //'bs_safari10_iphone7',
+                'bs_edgelatest_windows10',
+                // IE 11 failing with timezone issues
+                //'bs_ie11_windows10',
+                'bs_chromelatest_windows10',
+                'bs_firefoxlatest_windows10'
+                // Galaxy browser times out trying to connect
+                //'bs_galaxys8_chrome52'
+            ]
+        };
+    }
+
+    // If no browsers configured yet, default to Firefox.
+    if (karmaConfig.browsers.length === 0) {
+        console.log('Using Firefox...');
+        karmaConfig = {
+            ...karmaConfig,
+            browsers: ['Firefox']
+        };
+    }
+
+    config.set(karmaConfig);
+};
+
+
+function getCustomLaunchers() {
+    return {
+        FirefoxHeadless: {
+            base: 'Firefox',
+            flags: ['-headless']
+        },
+        /**
+         * BrowserStack browsers
+         * https://api.browserstack.com/automate/browsers.json
+         */
+        // Windows browsers
+        bs_edgelatest_windows10: {
+            base: 'BrowserStack',
+            browser: 'edge',
+            device: null,
+            browser_version: 'latest',
+            real_mobile: null,
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_ie11_windows10: {
+            base: 'BrowserStack',
+            browser: 'ie',
+            browser_version: '11',
+            os: 'windows',
+            os_version: '10'
+        },
+        bs_ie11_windows81: {
+            base: 'BrowserStack',
+            browser: 'ie',
+            browser_version: '11',
+            os: 'windows',
+            os_version: '8.1'
+        },
+        bs_ie9_windows7: {
+            base: 'BrowserStack',
+            browser: 'ie',
+            browser_version: '9',
+            os: 'windows',
+            os_version: '7'
+        },
+        bs_firefox48_windows10: {
+            base: 'BrowserStack',
+            browser: 'firefox',
+            browser_version: '48.0',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_firefoxlatest_windows10: {
+            base: 'BrowserStack',
+            browser: 'firefox',
+            browser_version: 'latest',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_firefox51_windows10: {
+            base: 'BrowserStack',
+            browser: 'firefox',
+            browser_version: '51.0',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_chromelatest_windows10: {
+            base: 'BrowserStack',
+            browser: 'chrome',
+            browser_version: 'latest',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_chrome58_windows10: {
+            base: 'BrowserStack',
+            browser: 'chrome',
+            browser_version: '58.0',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_chrome55_windows10: {
+            base: 'BrowserStack',
+            browser: 'chrome',
+            browser_version: '55.0',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_chrome52_windows10: {
+            base: 'BrowserStack',
+            browser: 'chrome',
+            browser_version: '52.0',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_chrome51_windows10: {
+            base: 'BrowserStack',
+            browser: 'chrome',
+            browser_version: '51.0',
+            os: 'Windows',
+            os_version: '10'
+        },
+        bs_chrome50_windows10: {
+            base: 'BrowserStack',
+            browser: 'chrome',
+            browser_version: '50.0',
+            os: 'Windows',
+            os_version: '10'
+        },
+
+        // OS X Browsers
+        bs_firefox_mac: {
+            base: 'BrowserStack',
+            browser: 'firefox',
+            browser_version: '48.0',
+            os: 'OS X',
+            os_version: 'Mountain Lion'
+        },
+        bs_safari8_mac: {
+            base: 'BrowserStack',
+            browser: 'safari',
+            browser_version: '8',
+            os: 'OS X',
+            os_version: 'Yosemite'
+        },
+        bs_safari9_mac: {
+            base: 'BrowserStack',
+            browser: 'safari',
+            browser_version: '9.1',
+            os: 'OS X',
+            os_version: 'El Capitan'
+        },
+        bs_safari10_mac: {
+            base: 'BrowserStack',
+            browser: 'safari',
+            browser_version: '10.0',
+            os: 'OS X',
+            os_version: 'Sierra'
+        },
+        bs_safarilatest_mac: {
+            base: 'BrowserStack',
+            browser: 'safari',
+            browser_version: 'latest',
+            os: 'OS X',
+            os_version: 'High Sierra'
+        },
+
+        // iOS browsers
+        bs_safari10_iphone7: {
+            base: 'BrowserStack',
+            browser: 'safari',
+            browser_version: '10.0',
+            device: 'iPhone 7',
+            os: 'ios',
+            os_version: '10.0'
+        },
+        bs_iphone5s: {
+            base: 'BrowserStack',
+            browser: 'safari',
+            device: 'iPhone 5S',
+            os: 'ios',
+            os_version: '7.0'
+        },
+        bs_iphone5: {
+            base: 'BrowserStack',
+            browser: 'safari',
+            device: 'iPhone 5',
+            os: 'ios',
+            os_version: '6.0'
+        },
+
+        // Android browsers
+        bs_galaxys8_chrome52: {
+            base: 'BrowserStack',
+            browser: 'chrome',
+            browser_version: '52',
+            device: 'Samsung Galaxy S8',
+            os: 'android',
+            os_version: '7.0',
+            real_mobile: true
+        },
+        bs_pixel_android: {
+            base: 'BrowserStack',
+            browser: 'android',
+            browser_version: null,
+            device: 'Google Pixel',
+            os: 'android',
+            os_version: '8.0',
+            real_mobile: true
+        }
+    };
+}
\ No newline at end of file
diff --git a/graph-server/Dockerfile b/graph-server/Dockerfile
index af5035afd..433384805 100644
--- a/graph-server/Dockerfile
+++ b/graph-server/Dockerfile
@@ -35,4 +35,4 @@ USER grapher
 
 EXPOSE 2929
 
-CMD DEBUG=${DEBUG} STATIC_ROOT=${STATIC_URL} OGC_SITE_ENDPOINT=${OGC_SITE_ENDPOINT} node src/site-store.js
\ No newline at end of file
+CMD DEBUG=${DEBUG} STATIC_ROOT=${STATIC_URL} OGC_SITE_ENDPOINT=${OGC_SITE_ENDPOINT} node src/index.js
\ No newline at end of file
diff --git a/graph-server/README.md b/graph-server/README.md
index 0c9437bd3..73c6e1dbf 100644
--- a/graph-server/README.md
+++ b/graph-server/README.md
@@ -19,7 +19,7 @@ one representing the time period specified and the second representing the same
 
 ## Running the server
 
-The entrypoint is `src/site-store.js`, which accepts the following environment
+The entrypoint is `src/index.js`, which accepts the following environment
 variables as arguments:
 
 - NODE_PORT: Port to run http server on. Default 2929.
@@ -30,7 +30,7 @@ variables as arguments:
 For example:
 
 ```bash
-% NODE_PORT=80 node src/site-store.js
+% NODE_PORT=80 node src/index.js
 ```
 
 Alternatively, if you want to use defaults as well as add DEBUG just use
diff --git a/wdfn-server/Makefile b/wdfn-server/Makefile
index df98fe2ab..12666c79c 100644
--- a/wdfn-server/Makefile
+++ b/wdfn-server/Makefile
@@ -35,7 +35,7 @@ wdfn-server/env:
 
 wdfn-env-requirements:
 	@echo 'Installing requirements.txt'
-	$(PIP) install -r wdfn-server/requirements.txt -r wdfn-server/requirements-dev.txt --trusted-host pypi.org --trusted-host files.pythonhosted.org
+	$(PIP) install -r wdfn-server/requirements.txt -r wdfn-server/requirements-dev.txt
 
 wdfn-server/instance/config.py:
 	@echo 'Creating wdfn-server/instance/config.py...'
diff --git a/wdfn-server/waterdata/__init__.py b/wdfn-server/waterdata/__init__.py
index 68e7d430d..2ee07f00e 100644
--- a/wdfn-server/waterdata/__init__.py
+++ b/wdfn-server/waterdata/__init__.py
@@ -7,7 +7,6 @@ import os
 import sys
 
 from flask import Flask
-# from flask_cors import CORS
 
 __version__ = '0.28.0dev'
 
@@ -37,7 +36,7 @@ app = Flask(__name__.split()[0], instance_relative_config=True)  # pylint: disab
 
 # Loads configuration information from config.py and instance/config.py
 app.config.from_object('config')
-# CORS(app, resources={r"/*": {"origins": "*"}}, expose_headers='Authorization')
+
 try:
     app.config.from_pyfile('config.py')
 except FileNotFoundError:
diff --git a/wdfn-server/waterdata/services/ogc.py b/wdfn-server/waterdata/services/ogc.py
index bb5da9818..e0f7913f7 100644
--- a/wdfn-server/waterdata/services/ogc.py
+++ b/wdfn-server/waterdata/services/ogc.py
@@ -14,11 +14,8 @@ def get_networks(network_cd=None):
     if network_cd:
         url = url + '/' + network_cd
 
-    print(url)
-
     response = execute_get_request(url, params={'f': 'json'})
 
-    print(response.status_code)
     if response.status_code != 200:
         return []
     try:
diff --git a/wdfn-server/waterdata/templates/base_network.html b/wdfn-server/waterdata/templates/base_network.html
index 036099135..64b716b05 100644
--- a/wdfn-server/waterdata/templates/base_network.html
+++ b/wdfn-server/waterdata/templates/base_network.html
@@ -1,7 +1,6 @@
 {% extends 'base_plain.html' %}
 
 {% block page_css %}
-      <link rel="stylesheet" href="{{ 'vendor.css' | asset_url }}">
       <link rel="stylesheet" href="{{ 'network.css' | asset_url }}">
 {% endblock %}
 
@@ -11,10 +10,10 @@
 {% endblock %}
 
 {% block body %}
-{% include 'partials/base.html' %}
-<main id="main-content" class="grid-container content-container usa-prose">
-    {% block content %}{% endblock %}
-</main>
-{% include 'partials/footer.html' %}
-{% block extra_body %}{% endblock %}
+    {% include 'partials/base.html' %}
+    <main id="main-content" class="grid-container content-container usa-prose">
+        {% block content %}{% endblock %}
+    </main>
+    {% include 'partials/footer.html' %}
+    {% block extra_body %}{% endblock %}
 {% endblock %}
diff --git a/wdfn-server/waterdata/templates/macros/components.html b/wdfn-server/waterdata/templates/macros/components.html
index 4fd3017d2..b0eb5d089 100644
--- a/wdfn-server/waterdata/templates/macros/components.html
+++ b/wdfn-server/waterdata/templates/macros/components.html
@@ -38,10 +38,38 @@
     </div>
 {%- endmacro %}
 
-{% macro NetworkMapComponent(network_cd, extent) -%}
-    <div class="wdfn-component" data-component="network-map" data-networkcd="{{ network_cd }}" data-extent="{{  extent }}">
+{% macro NetworkComponent(network_cd, extent) -%}
+     <h3 id="network_map_heading">Network Map:</h3>
+                <p id="overload-map"></p>
+    <div class="wdfn-component" data-component="network" data-networkcd="{{ network_cd }}" data-extent="{{  extent }}">
         <div id="network-map"></div>
     </div>
+    <h3 id="network_table_heading">Link Table:</h3>
+    <p id="overload-table"></p>
+    <div id="link-list">
+        <table id="link-table" class="usa-table">
+            <thead>
+                <tr>
+                    <th colspan="2">
+                        <input id="search-name" class="search" placeholder="Search Name">
+                        <div class="table-pagination">
+                            <ul class="pagination"></ul>
+                        </div>
+                    </th>
+                </tr>
+                <tr>
+                    <th>
+                        <button type="button" class="sort" data-sort="name">Description<i class="caret"></i></button>
+                    </th>
+                    <th>
+                        Link
+                    </th>
+                </tr>
+            </thead>
+            <tbody class="list"></tbody>
+        </table>
+    </div>
+
 {%- endmacro %}
 
 
diff --git a/wdfn-server/waterdata/templates/networks.html b/wdfn-server/waterdata/templates/networks.html
index e076afd4b..250efd0cc 100644
--- a/wdfn-server/waterdata/templates/networks.html
+++ b/wdfn-server/waterdata/templates/networks.html
@@ -17,14 +17,9 @@
                     <h2>Network: {{ network_cd }} </h2>
                     <h3>Description: Not Available</h3>
                 {% endif %}
-                <h3 id="network_map_heading">Network Map:</h3>
-                <p id="overload-map"></p>
-               {{ components.NetworkMapComponent(network_cd, extent) }}
-                <h3 id="network_table_heading">Link Table:</h3>
-                <p id="overload-table"></p>
-                <span>Search Name:</span> <input id="table-search" />
-                <div id="link-list" >
-                </div>
+
+               {{ components.NetworkComponent(network_cd, extent) }}
+
             {% else %}
                 <p>Select a Network</p>
                 <table class="usa-table">
diff --git a/wdfn-server/waterdata/utils.py b/wdfn-server/waterdata/utils.py
index 316090a10..4bcafc7d9 100644
--- a/wdfn-server/waterdata/utils.py
+++ b/wdfn-server/waterdata/utils.py
@@ -22,6 +22,7 @@ def execute_get_request(hostname, path=None, params=None):
 
     """
     target = urljoin(hostname, path)
+    print(target)
     try:
         resp = r.get(target, params=params)
     except (r.exceptions.Timeout, r.exceptions.ConnectionError) as err:
-- 
GitLab