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