diff --git a/assets/src/scripts/components/hydrograph/index.js b/assets/src/scripts/components/hydrograph/index.js index ebaa25b14b684187845c4ecfca2257a073529dd8..190b649069c8fd67b8c2c96a9149b650fdc71603 100644 --- a/assets/src/scripts/components/hydrograph/index.js +++ b/assets/src/scripts/components/hydrograph/index.js @@ -8,7 +8,7 @@ const { select } = require('d3-selection'); const { createStructuredSelector } = require('reselect'); const { addSVGAccessibility } = require('../../accessibility'); -const { USWDS_MEDIUM_SCREEN, USWDS_SMALL_SCREEN, STATIC_URL } = require('../../config'); +const { USWDS_SMALL_SCREEN, STATIC_URL } = require('../../config'); const { dispatch, link, provide } = require('../../lib/redux'); const { Actions } = require('../../store'); const { mediaQuery } = require('../../utils'); @@ -27,7 +27,7 @@ const { allTimeSeriesSelector, isVisibleSelector, titleSelector, const { createTooltipFocus, createTooltipText } = require('./tooltip'); const { coerceStatisticalSeries } = require('./statistics'); -const { getCurrentDateRange } = require('../../selectors/timeSeriesSelector'); +const { getCurrentDateRange, isLoadingTS } = require('../../selectors/timeSeriesSelector'); const drawMessage = function (elem, message) { @@ -407,7 +407,16 @@ const attachToNode = function (store, node, {siteno} = {}) { store.dispatch(Actions.resizeUI(window.innerWidth, node.offsetWidth)); select(node) - .call(provide(store)) + .call(provide(store)); + select(node) + .call(link(function(elem, isLoadingCurrentTS) { + elem.select('.loading-indicator').remove(); + if (isLoadingCurrentTS) { + select(elem).append('i') + .attr('class', 'fas fa-spinner fa-spin'); + } + }, isLoadingTS('current:P7D'))); + select(node) .call(link(createDaterangeControls, createStructuredSelector({ siteno: () => siteno, showControls: hasTimeSeriesWithPoints('current', 'P7D') diff --git a/assets/src/scripts/components/map/index.spec.js b/assets/src/scripts/components/map/index.spec.js index b71f7376c036a9a57bbd79495abc4094eae532d7..cc6b46a56729780b9dbdd3bf54edb6aadc63e308 100644 --- a/assets/src/scripts/components/map/index.spec.js +++ b/assets/src/scripts/components/map/index.spec.js @@ -4,7 +4,7 @@ const proxyquire = require('proxyquireify')(require); const { attachToNode } = require('./index'); const { configureStore } = require('../../store'); -fdescribe('map module', () => { +describe('map module', () => { let mapNode; let store; let floodDataMock; diff --git a/assets/src/scripts/selectors/timeSeriesSelector.js b/assets/src/scripts/selectors/timeSeriesSelector.js index 9bc234e44dd256c7aee533c6c314f15240df326f..f5ded6053a3d1e786d2b5d98d9e38079a9c1356e 100644 --- a/assets/src/scripts/selectors/timeSeriesSelector.js +++ b/assets/src/scripts/selectors/timeSeriesSelector.js @@ -6,13 +6,17 @@ const { createSelector } = require('reselect'); */ export const getVariables = state => state.series.variables ? state.series.variables : null; +export const getMethods = state => state.series.methods ? state.series.methods : null; + +export const getQueryInfo = state => state.series.queryInfo || {}; + + export const getCurrentVariableID = state => state.timeSeriesState.currentVariableID; export const getCurrentDateRange = state => state.timeSeriesState.currentDateRange; -export const getMethods = state => state.series.methods ? state.series.methods : null; +export const getLoadingTsKeys = state => state.timeSeriesState.loadingTSKeys; -export const getQueryInfo = state => state.series.queryInfo || {}; /* * Selectors the return derived data from the state @@ -111,3 +115,7 @@ export const getRequestTimeRange = memoize((tsKey, period, parmCd) => createSele } )); +export const isLoadingTS = memoize(tsKey => createSelector( + getLoadingTsKeys, + (loadingTSKeys) => loadingTSKeys.includes(tsKey) +)); diff --git a/assets/src/scripts/store/index.js b/assets/src/scripts/store/index.js index 66eebd5d4c15b693867b64b5a1e3a6bdb9a6b922..defa7856fdbf4f34ba103404fe5fb6781e3b22b3 100644 --- a/assets/src/scripts/store/index.js +++ b/assets/src/scripts/store/index.js @@ -50,9 +50,12 @@ export const Actions = { retrieveTimeSeries(siteno, params=null) { return function (dispatch, getState) { const currentState = getState(); + const requestKey = getTsRequestKey('current', 'P7D')(currentState); + dispatch(Actions.addTimeSeriesLoading([requestKey])); + const timeSeries = getTimeSeries({sites: [siteno], params}).then( series => { - const requestKey = getTsRequestKey('current', 'P7D')(currentState); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); const collection = normalize(series, requestKey); // Get the start/end times of this request's range. @@ -77,6 +80,7 @@ export const Actions = { return {collection, startTime, endTime}; }, () => { + dispatch(Actions.removeTimeSeriesLoading([requestKey])); dispatch(Actions.resetTimeSeries(getTsRequestKey('current', 'P7D')(currentState))); dispatch(Actions.toggleTimeSeries('current', false)); return { @@ -193,6 +197,18 @@ export const Actions = { type: 'TIMESERIES_PLAY_STOP' }; }, + addTimeSeriesLoading(tsKeys) { + return { + type: 'TIMESERIES_LOADING_ADD', + tsKeys + }; + }, + removeTimeSeriesLoading(tsKeys) { + return { + type: 'TIMESERIES_LOADING_REMOVE', + tsKeys + }; + }, setFloodFeatures(stages, extent) { return { type: 'SET_FLOOD_FEATURES', @@ -289,7 +305,8 @@ export const configureStore = function (initialState) { currentDateRange: 'P7D', currentVariableID: null, cursorOffset: null, - audiblePlayId: null + audiblePlayId: null, + loadingTSKeys: [] }, floodState: { gageHeight: null diff --git a/assets/src/scripts/store/timeSeriesStateReducer.js b/assets/src/scripts/store/timeSeriesStateReducer.js index f3222f341295714ae98ecef5d192bc035251aef9..46529150e8925f9a73e057296a254a4fe6ab1863 100644 --- a/assets/src/scripts/store/timeSeriesStateReducer.js +++ b/assets/src/scripts/store/timeSeriesStateReducer.js @@ -48,6 +48,20 @@ const timeSeriesPlayStop = function(timeSeriesState) { }; }; +const addLoadingTimeSeries = function(timeSeriesState, action) { + return { + ...timeSeriesState, + loadingTSKeys: timeSeriesState.loadingTSKeys.concat([action.tsKeys]) + }; +}; + +const removeLoadingTimeSeries = function(timeSeriesState, action) { + return { + ...timeSeriesState, + loadingTSKeys: timeSeriesState.loadingTSKeys.filter((tsKey) => action.tsKeys.includes(tsKey)) + }; +}; + /* * Slice reducer */ @@ -59,6 +73,8 @@ export const timeSeriesStateReducer = function(timeSeriesState={}, action) { case 'SET_CURSOR_OFFSET': return setCursorOffset(timeSeriesState, action); case 'TIMESERIES_PLAY_ON': return timeSeriesPlayOn(timeSeriesState, action); case 'TIMESERIES_PLAY_STOP': return timeSeriesPlayStop(timeSeriesState, action); + case 'TIMESERIES_LOADING_ADD': return addLoadingTimeSeries(timeSeriesState, action); + case 'TIMESERIES_LOADING_REMOVE': return removeLoadingTimeSeries(timeSeriesState, action); default: return timeSeriesState; } };