From b5a283f1b4ca35cd552cc7a86dd6dc4062871829 Mon Sep 17 00:00:00 2001
From: mbucknell <mbucknell@usgs.gov>
Date: Fri, 12 Feb 2021 07:23:12 -0600
Subject: [PATCH] Trying to simplify the date-controls. I have decided to wait
 on this and move on to other controls in the hydrograph.

---
 .../components/hydrograph/date-controls.js    | 153 ++++++++----------
 .../components/hydrograph/index.js            |  97 +++++------
 .../selectors/hydrograph-state-selector.js    |  25 +++
 .../templates/macros/components.html          |   1 -
 4 files changed, 146 insertions(+), 130 deletions(-)

diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js b/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js
index bafd9163c..9361c5111 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js
@@ -6,70 +6,70 @@ import components from 'uswds/src/js/components';
 import config from 'ui/config';
 import {link} from 'ui/lib/d3-redux';
 
-import {drawLoadingIndicator} from 'd3render/loading-indicator';
-
-import {getAllGroundwaterLevels} from 'ml/selectors/discrete-data-selector';
-import {
-    isLoadingTS,
-    hasAnyTimeSeries,
-    getUserInputsForSelectingTimespan,
-    getCustomTimeRange,
-    getCurrentParmCd
-} from 'ml/selectors/time-series-selector';
-import {getIanaTimeZone} from 'ml/selectors/time-zone-selector';
-
-import {Actions as ivTimeSeriesDataActions} from 'ml/store/instantaneous-value-time-series-data';
-import {Actions as ivTimeSeriesStateActions} from 'ml/store/instantaneous-value-time-series-state';
-
-
-export const drawDateRangeControls = function(elem, store, siteno) {
-    const DATE_RANGE = [{
-        name: '7 days',
-        period: 'P7D'
-    }, {
-        name: '30 days',
-        period: 'P30D'
-    }, {
-        name: '1 year',
-        period: 'P1Y'
-    }, {
-        name: 'Custom',
-        period: 'custom',
+import {getInputsForRetrieval, getSelectedDateRange, getSelectedCustomTimeRange} from 'ml/selectors/hydrograph-state-selector';
+
+import {retrieveHydrographData} from 'ml/store/hydrograph-data';
+import {clearGraphBrushOffset, setSelectedCustomTimeRange, setSelectedDateRange} from 'ml/store/hydrograph-state';
+
+const DATE_RANGE = [{
+    name: '7 days',
+    period: 'P7D'
+}, {
+    name: '30 days',
+    period: 'P30D'
+}, {
+    name: '1 year',
+    period: 'P365D'
+}, {
+    name: 'Custom',
+    period: 'custom',
+    ariaExpanded: false
+}];
+const CUSTOM_TIMEFRAME_RADIO_BUTTON_DETAILS = [
+    {
+        id: 'custom-input-days-before-today',
+        value: 'days',
+        text: 'Days before today',
         ariaExpanded: false
-    }];
-    const CUSTOM_TIMEFRAME_RADIO_BUTTON_DETAILS = [
-        {
-            id: 'custom-input-days-before-today',
-            value: 'days',
-            text: 'Days before today',
-            checked: true,
-            ariaExpanded: false
-        },
-        {
-            id: 'custom-input-calendar-days',
-            value: 'calendar',
-            text: 'Calendar days',
-            checked: false,
-            ariaExpanded: false
-        }
-    ];
+    },
+    {
+        id: 'custom-input-calendar-days',
+        value: 'calendar',
+        text: 'Calendar days',
+        ariaExpanded: false
+    }
+];
+
+const isCustomPeriod = function(dateRange) {
+    return dateRange !== 'custom' && !DATE_RANGE.find(range => range.period === dateRange);
+}
+const showCustomContainer = function(dateRange) {
+    return dateRange === 'custom' ? true : !DATE_RANGE.find(range => range.period === dateRange);
+}
+
+export const drawDateRangeControls = function(elem, store, siteno, initialPeriod, initialStartTime, initialEndTime) {
+    let isCustomPeriod = false;
+    let isCustomCalendarDays = false;
+    if (initialPeriod) {
+        store.dispatch(setSelectedDateRange(initialPeriod));
+        isCustomPeriod = !DATE_RANGE.find(range => range.period === period);
+    } else if (initialStartTime && initialEndTime) {
+        isCustomCalendarDays = true;
+        store.dispatch(setSelectedCustomTimeRange(
+            DateTime.fromISO(initialStartTime, {zone: config.locationTimeZone}).toMillis(),
+            DateTime.fromISO(initialEndTime, {zone: config.locationTimeZone}).toMillis()));
+    }
+    const isCustomSelected = isCustomPeriod || isCustomCalendarDays;
 
     const containerRadioGroupMainSelectButtons = elem.insert('div', ':nth-child(2)')
         .attr('id', 'ts-daterange-select-container')
         .attr('role', 'radiogroup')
-        .attr('aria-label', 'Time interval select')
-        .call(link(store,function(container, showControls) {
-            container.attr('hidden', showControls ? null : true);
-        }, (state) => {
-            return hasAnyTimeSeries(state) || getAllGroundwaterLevels(state);
-        }));
+        .attr('aria-label', 'Time interval select');
 
     // Add a container that holds the custom selection radio buttons and the form fields
     const containerRadioGroupAndFormButtons = elem.insert('div', ':nth-child(3)')
         .attr('class', 'container-radio-group-and-form-buttons')
-        .call(link(store, (container, userInputsForSelectingTimespan) => {
-            container.attr('hidden', userInputsForSelectingTimespan.mainTimeRangeSelectionButton === 'custom' ? null : true);
-        }, getUserInputsForSelectingTimespan));
+        .attr('hidden', isCustomSelected ? null : true);
 
     const containerRadioGroupCustomSelectButtons = containerRadioGroupAndFormButtons.append('div')
         .attr('id', 'ts-custom-date-radio-group')
@@ -80,18 +80,14 @@ export const drawDateRangeControls = function(elem, store, siteno) {
         .attr('id', 'ts-custom-days-before-today-select-container')
         .attr('class', 'usa-form')
         .attr('aria-label', 'Custom date by days before today specification')
-    .call(link(store, (container, userInputsForSelectingTimespan) => {
-        container.attr('hidden', userInputsForSelectingTimespan.customTimeRangeSelectionButton === 'days-input' && userInputsForSelectingTimespan.mainTimeRangeSelectionButton === 'custom' ? null : true);
-    }, getUserInputsForSelectingTimespan));
+        .attr('hidden', isCustomCalendarDays ? true : null);
 
     const containerCustomCalendarDays = containerRadioGroupAndFormButtons.append('div')
         .attr('id', 'ts-customdaterange-select-container')
         .attr('role', 'customdate')
         .attr('class', 'usa-form')
         .attr('aria-label', 'Custom date specification')
-        .call(link(store, (container, userInputsForSelectingTimespan) => {
-            container.attr('hidden', userInputsForSelectingTimespan.customTimeRangeSelectionButton === 'calendar-input' && userInputsForSelectingTimespan.mainTimeRangeSelectionButton === 'custom' ? null : true);
-        }, getUserInputsForSelectingTimespan));
+        .attr('hidden', isCustomCalendarDays ? null : true);
 
     const createRadioButtonsForCustomDaterangeSelection = function(containerRadioGroupCustomSelectButtons) {
         containerRadioGroupCustomSelectButtons.append('p').text('Enter timespan using');
@@ -108,23 +104,19 @@ export const drawDateRangeControls = function(elem, store, siteno) {
             .attr('id', d => `${d.value}-input`)
             .attr('class', 'usa-radio__input')
             .attr('value', d => d.value)
-            .property('checked', d => d.checked)
+            .property('checked', d => d.value === 'days' && !isCustomCalendarDays || isCustomCalendarDays)
             .attr('ga-on', 'click')
-            .attr('aria-expanded', d => d.ariaExpanded)
+            .attr('aria-expanded', d => d.value === 'days' && !isCustomCalendarDays || isCustomCalendarDays)
             .attr('ga-event-category', 'TimeSeriesGraph')
             .attr('ga-event-action', d => `changeDateRangeWith${d.value}`)
-            .on('change', function() {
-                const selected = listItemForCustomSelectRadioButtons.select('input:checked');
-                const selectedVal = selected.attr('id');
-                store.dispatch(ivTimeSeriesStateActions.setUserInputsForSelectingTimespan('customTimeRangeSelectionButton', selectedVal));
+            .on('change', function(_, d) {
+                containerCustomDaysBeforeToday.attr('hidden', d.value === 'days' ? null : true);
+                containerCustomCalendarDays.attr('hidden', d.value === 'calendar' ? null : true);
             });
         listItemForCustomSelectRadioButtons.append('label')
             .attr('class', 'usa-radio__label')
             .attr('for', (d) => `${d.value}-input`)
             .text((d) => d.text);
-        listItemForCustomSelectRadioButtons.call(link(store, (elem, userInputsForSelectingTimespan) => {
-            elem.select(`#${userInputsForSelectingTimespan.customTimeRangeSelectionButton}`).property('checked', true);
-        }, getUserInputsForSelectingTimespan));
     };
 
     const createControlsForSelectingTimeSpanInDaysFromToday = function() {
@@ -145,7 +137,9 @@ export const drawDateRangeControls = function(elem, store, siteno) {
             .attr('id', 'with-hint-input-days-from-today')
             .attr('maxlength', `${config.MAX_DIGITS_FOR_DAYS_FROM_TODAY}`)
             .attr('name', 'with-hint-input-days-from-today')
+            .attr('value', isCustomPeriod ? initialPeriod.slice(1, -2) : '')
             .attr('aria-describedby', 'with-hint-input-days-from-today-info with-hint-input-days-from-today-hint');
+
         numberOfDaysSelection.append('span')
             .text(`${config.MAX_DIGITS_FOR_DAYS_FROM_TODAY} digits allowed`)
             .attr('id', 'with-hint-input-days-from-today-info')
@@ -162,12 +156,7 @@ export const drawDateRangeControls = function(elem, store, siteno) {
         customDaysBeforeTodayAlertBody.append('h3')
             .attr('class', 'usa-alert__heading')
             .text('Requirements');
-        numberOfDaysSelection.call(link(store, (container, userInputsForSelectingTimespan) => {
-            if (userInputsForSelectingTimespan.mainTimeRangeSelectionButton === 'custom' && userInputsForSelectingTimespan.customTimeRangeSelectionButton === 'days-input') {
-                container.select('#with-hint-input-days-from-today')
-                    .property('value', userInputsForSelectingTimespan.numberOfDaysFieldValue);
-            }
-        }, getUserInputsForSelectingTimespan));
+
         // Adds controls for the 'days before today' submit button
         const daysBeforeTodaySubmitContainer = containerCustomDaysBeforeToday.append('div')
             .attr('class', 'submit-button');
@@ -189,14 +178,12 @@ export const drawDateRangeControls = function(elem, store, siteno) {
                     customDaysBeforeTodayValidationContainer.attr('hidden', null);
                 } else {
                     customDaysBeforeTodayValidationContainer.attr('hidden', true);
-                    const parameterCode = getCurrentParmCd(store.getState());
 
-                    store.dispatch(ivTimeSeriesStateActions.setUserInputsForSelectingTimespan('numberOfDaysFieldValue', userSpecifiedNumberOfDays));
-                    store.dispatch(ivTimeSeriesDataActions.retrieveCustomTimePeriodIVTimeSeries(
-                        siteno,
-                        parameterCode,
-                        formattedPeriodQueryParameter
-                    )).then(() => store.dispatch(ivTimeSeriesStateActions.clearIVGraphBrushOffset()));
+                    store.dispatch(setUserInputsForSelectingTimespan('numberOfDaysFieldValue', userSpecifiedNumberOfDays));
+                    store.dispatch(setUserInputsForSelectingTimespan('mainTimeRangeSelectionButton', 'custom'));
+                    store.dispatch(setSelectedDateRange(formattedPeriodQueryParameter));
+                    store.dispatch(retrieveHydrographData(siteno, getInputsForRetrieval(store.getState())))
+                        .then(() => store.dispatch(clearGraphBrushOffset()));
                 }
             });
     };
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/index.js b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
index d7d224ee5..2802aeb8b 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/index.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
@@ -19,11 +19,13 @@ import {drawLoadingIndicator} from 'd3render/loading-indicator';
 //import {hasAnyVariables, getCurrentVariableID, getCurrentParmCd, getVariables} from 'ml/selectors/time-series-selector';
 
 import {retrieveHydrographData} from 'ml/store/hydrograph-data';
+import {setSelectedParameterCode} from 'ml/store/hydrograph-state';
+
 //import {Actions as ivTimeSeriesDataActions} from 'ml/store/instantaneous-value-time-series-data';
 //import {Actions as ivTimeSeriesStateActions} from 'ml/store/instantaneous-value-time-series-state';
 //import {Actions as statisticsDataActions} from 'ml/store/statistics-data';
 //import {Actions as timeZoneActions} from 'ml/store/time-zone';
-//import {Actions as floodDataActions} from 'ml/store/flood-inundation';
+import {Actions as floodDataActions} from 'ml/store/flood-inundation';
 
 //import {drawDateRangeControls} from './date-controls';
 //import {drawDataTables} from './data-table';
@@ -56,8 +58,6 @@ export const attachToNode = function(store,
                                          siteno,
                                          agencyCode,
                                          sitename,
-                                         latitude,
-                                         longitude,
                                          parameterCode,
                                          compare,
                                          period,
@@ -78,9 +78,6 @@ export const attachToNode = function(store,
         .select('.loading-indicator-container')
         .call(drawLoadingIndicator, {showLoadingIndicator: true, sizeClass: 'fa-3x'});
 
-    // Fetch waterwatch flood levels
-    //store.dispatch(floodDataActions.retrieveWaterwatchData(siteno));
-     // Need to set default parameter code in server and insert in markup */
     const fetchDataPromise = store.dispatch(retrieveHydrographData(siteno, {
         parameterCode: parameterCode,
         period: startDT && endDT ? null : period || 'P7D',
@@ -89,54 +86,62 @@ export const attachToNode = function(store,
         loadCompare: false,
         loadMedian: false
     }));
+
+
+    // Fetch waterwatch flood levels - TODO: consider only fetching when gage height is requested
+    store.dispatch(floodDataActions.retrieveWaterwatchData(siteno));
+
     fetchDataPromise.then(() => {
         console.log('Finished fetching Hydrograph data');
         nodeElem
             .select('.loading-indicator-container')
             .call(drawLoadingIndicator, {showLoadingIndicator: false, sizeClass: 'fa-3x'});
 
-            // Initial data has been fetched. We can render the hydrograph elements
-            // Initialize method picker before rendering time series
-            let graphContainer = nodeElem.select('.graph-container')
-                .call(drawMethodPicker, store, timeSeriesId)
-                .call(drawTimeSeriesGraph, store, siteno, agencyCode, sitename, showMLName, !showOnlyGraph);
-
-            if (!showOnlyGraph) {
-                graphContainer
-                    .call(drawTooltipCursorSlider, store)
-                    .call(drawGraphBrush, store);
-            }
-            graphContainer.append('div')
-                .classed('ts-legend-controls-container', true)
-                .call(drawTimeSeriesLegend, store);
-            // Add UI interactive elements, data table  and the provisional data alert.
+        // Initial data has been fetched. We can render the hydrograph elements
+        // Initialize method picker before rendering the graph in order to set the selected method id
+        if (!showOnlyGraph) {
+            nodeElem.call(drawMethodPicker, store, timeSeriesId);
+        }
+        let graphContainer = nodeElem.select('.graph-container');
+        graphContainer.call(drawTimeSeriesGraph, store, siteno, agencyCode, sitename, showMLName, !showOnlyGraph);
+
+        if (!showOnlyGraph) {
+            graphContainer
+                .call(drawTooltipCursorSlider, store)
+                .call(drawGraphBrush, store);
+        }
+        const legendControlsContainer = graphContainer.append('div')
+            .classed('ts-legend-controls-container', true)
+            .call(drawTimeSeriesLegend, store);
+
+        if (!showOnlyGraph) {
+            /*
+            nodeElem
+                .call(drawDateRangeControls, store, siteno);
+            legendControlsContainer
+                .call(drawGraphControls, store);
+
+            nodeElem.select('#iv-graph-list-container')
+                .call(renderDownloadLinks, store, siteno);
+
+            nodeElem.select('#iv-data-table-container')
+                .call(drawDataTables, store);
+*/
+            // Set the parameter code explictly. We may eventually set this within the parameter selection table
+            store.dispatch(setSelectedParameterCode(parameterCode));
             /*
-            if (!showOnlyGraph) {
-                nodeElem
-                    .call(drawMethodPicker, store)
-                    .call(drawDateRangeControls, store, siteno);
-
-                nodeElem.select('.ts-legend-controls-container')
-                    .call(drawGraphControls, store);
-
-                nodeElem.select('#iv-graph-list-container')
-                    .call(renderDownloadLinks, store, siteno);
-
-                nodeElem.select('#iv-data-table-container')
-                    .call(drawDataTables, store);
-                //TODO: Find out why putting this before drawDataTable causes the tests to not work correctly
-                nodeElem.select('.select-time-series-container')
-                    .call(link(store, plotSeriesSelectTable, createStructuredSelector({
-                        siteno: () => siteno,
-                        availableParameterCodes: getAvailableParameterCodes,
-                        lineSegmentsByParmCd: getLineSegmentsByParmCd('current', 'P7D'),
-                        timeSeriesScalesByParmCd: getTimeSeriesScalesByParmCd('current', 'P7D', SPARK_LINE_DIM)
-                    }), store));
-
-                renderTimeSeriesUrlParams(store);
-            }
+            //TODO: Find out why putting this before drawDataTable causes the tests to not work correctly
+            nodeElem.select('.select-time-series-container')
+                .call(link(store, plotSeriesSelectTable, createStructuredSelector({
+                    siteno: () => siteno,
+                    availableParameterCodes: getAvailableParameterCodes,
+                    lineSegmentsByParmCd: getLineSegmentsByParmCd('current', 'P7D'),
+                    timeSeriesScalesByParmCd: getTimeSeriesScalesByParmCd('current', 'P7D', SPARK_LINE_DIM)
+                }), store));
+
+            renderTimeSeriesUrlParams(store);
             */
-
+        }
     });
 
 };
diff --git a/assets/src/scripts/monitoring-location/selectors/hydrograph-state-selector.js b/assets/src/scripts/monitoring-location/selectors/hydrograph-state-selector.js
index a671b5dbf..d2a978953 100644
--- a/assets/src/scripts/monitoring-location/selectors/hydrograph-state-selector.js
+++ b/assets/src/scripts/monitoring-location/selectors/hydrograph-state-selector.js
@@ -1,4 +1,7 @@
 
+import {DateTime} from 'luxon';
+import {createSelector} from 'reselect';
+
 export const isCompareIVDataVisible = state => state.hydrographState.showCompareIVData || false;
 export const isMedianDataVisible = state => state.hydrographState.showMedianData || false;
 
@@ -10,3 +13,25 @@ export const getGraphCursorOffset = state => state.hydrographState.graphCursorOf
 export const getGraphBrushOffset = state => state.hydrographState.graphBrushOffset || null;
 export const getUserInputsForTimeRange = state => state.hydrographState.userInputsForTimeRange || null;
 
+export const getInputsForRetrieval = createSelector(
+    getSelectedParameterCode,
+    getSelectedDateRange,
+    getSelectedCustomTimeRange,
+    isCompareIVDataVisible,
+    isMedianDataVisible,
+    (parameterCode, selectedDateRange, selectedCustomTimeRange, loadCompare, loadMedian) => {
+        const isCustomTime = selectedDateRange === 'custom';
+        const period = isCustomTime ? null : selectedDateRange;
+        const startTime = isCustomTime ? DateTime.toISO(DateTime.fromMillis(selectedCustomTimeRange.start)) : null;
+        const endTime = isCustomTime ? DateTime.toISO(DateTime.fromMillis(selectedCustomTimeRange.end)) : null;
+
+        return {
+            parameterCode,
+            period,
+            startTime,
+            endTime,
+            loadCompare,
+            loadMedian
+        };
+    }
+);
diff --git a/wdfn-server/waterdata/templates/macros/components.html b/wdfn-server/waterdata/templates/macros/components.html
index 19de1f983..4e2729d9b 100644
--- a/wdfn-server/waterdata/templates/macros/components.html
+++ b/wdfn-server/waterdata/templates/macros/components.html
@@ -1,6 +1,5 @@
 {% macro TimeSeriesComponent(site_data, default_parameter_code) -%}
     <div class="wdfn-component" data-component="hydrograph" data-siteno="{{ site_data.site_no }}"
-         data-latitude="{{ site_data.dec_lat_va }}" data-longitude="{{ site_data.dec_long_va }}"
          data-agency-cd="{{ site_data.agency_cd }}" data-sitename="{{ site_data.station_nm }}"
          data-parameter-code="{{ default_parameter_code }}">
         <div class="loading-indicator-container"></div>
-- 
GitLab