From 3342b2190b982b6637f85be8e5ffd15ec5a649be Mon Sep 17 00:00:00 2001
From: mbucknell <mbucknell@usgs.gov>
Date: Thu, 25 Mar 2021 08:02:47 -0500
Subject: [PATCH] Start of refactor of the time-span-controls starting with the
 calendar dates.

---
 .../components/hydrograph/graph-controls.js   |   8 +-
 .../components/hydrograph/index.js            |  12 +-
 .../components/hydrograph/select-actions.js   |   7 +-
 .../hydrograph/time-span-controls.js          | 155 ++++--------------
 .../scripts/monitoring-location/url-params.js |  37 ++---
 .../styles/components/hydrograph/_app.scss    |  56 ++-----
 6 files changed, 70 insertions(+), 205 deletions(-)

diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js b/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
index 7ba60f0c9..fb2a2ac82 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
@@ -3,7 +3,7 @@ import {createStructuredSelector} from 'reselect';
 import config from 'ui/config';
 import {link} from 'ui/lib/d3-redux';
 
-import {getSelectedParameterCode, getSelectedDateRange} from 'ml/selectors/hydrograph-state-selector';
+import {getSelectedParameterCode, getSelectedTimeSpan} from 'ml/selectors/hydrograph-state-selector';
 import {getTimeRange} from 'ml/selectors/hydrograph-data-selector';
 
 import {retrieveMedianStatistics, retrievePriorYearIVData} from 'ml/store/hydrograph-data';
@@ -58,15 +58,15 @@ export const drawGraphControls = function(elem, store, siteno) {
             }
         })
         // Sets the state of the toggle
-        .call(link(store,function(elem, {checked, selectedDateRange, parameterCode}) {
+        .call(link(store,function(elem, {checked, selectedTimeSpan, parameterCode}) {
             elem.property('checked', checked)
                 .attr('disabled',
                 hasIVData(parameterCode) &&
-                selectedDateRange !== 'custom' && config.ALLOW_COMPARE_DATA_FOR_PERIODS.includes(selectedDateRange) ?
+                typeof selectedTimeSpan === 'string' && config.ALLOW_COMPARE_DATA_FOR_PERIODS.includes(selectedTimeSpan) ?
                 null : true);
         }, createStructuredSelector({
             checked: isVisible('compare'),
-            selectedDateRange: getSelectedDateRange,
+            selectedTimeSpan: getSelectedTimeSpan,
             parameterCode: getSelectedParameterCode
         })));
     compareControlDiv.append('label')
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/index.js b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
index f191afb7e..87304de65 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/index.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
@@ -12,7 +12,7 @@ import {renderTimeSeriesUrlParams} from 'ml/url-params';
 
 import {retrieveHydrographData} from 'ml/store/hydrograph-data';
 import {retrieveHydrographParameters} from 'ml/store/hydrograph-parameters';
-import {setSelectedParameterCode, setCompareDataVisibility, setSelectedCustomDateRange, setSelectedDateRange,
+import {setSelectedParameterCode, setCompareDataVisibility, setSelectedTimeSpan,
     setSelectedIVMethodID
 } from 'ml/store/hydrograph-state';
 
@@ -90,12 +90,14 @@ export const attachToNode = function(store,
     store.dispatch(setSelectedParameterCode(parameterCode));
     store.dispatch(setCompareDataVisibility(initialLoadCompare));
     if (period) {
-        store.dispatch(setSelectedDateRange(period));
+        store.dispatch(setSelectedTimeSpan(period));
     } else if (startDT && endDT) {
-        store.dispatch(setSelectedDateRange('custom'));
-        store.dispatch(setSelectedCustomDateRange(startDT, endDT));
+        store.dispatch(setSelectedTimeSpan({
+            start: startDT,
+            end: endDT
+        }));
     } else {
-        store.dispatch(setSelectedDateRange('P7D'));
+        store.dispatch(setSelectedTimeSpan('P7D'));
     }
 
     // Fetch waterwatch flood levels
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/select-actions.js b/assets/src/scripts/monitoring-location/components/hydrograph/select-actions.js
index 1ee3449d6..c8802acdc 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/select-actions.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/select-actions.js
@@ -1,8 +1,6 @@
 import {select} from 'd3-selection';
 
-import {getSelectedCustomDateRange, getSelectedDateRange} from 'ml/selectors/hydrograph-state-selector';
-
-import {drawDateRangeControls} from './date-controls';
+import {drawTimeSpanControls} from './time-span-controls';
 import {drawDownloadForm} from './download-data';
 
 /*
@@ -13,6 +11,7 @@ const appendButton = function(listContainer, {faIcon, buttonLabel, idOfDivToCont
         .attr('class', 'usa-button-group__item')
         .append('button')
             .attr('class', 'usa-button')
+            .attr('role', 'checkbox')
             .attr('aria-expanded', false)
             .attr('aria-controls', idOfDivToControl)
             .attr('ga-on', 'click')
@@ -69,7 +68,7 @@ export const drawSelectActions = function(container, store, siteno) {
     container.append('div')
         .attr('id', 'change-time-span-container')
         .attr('hidden', true)
-        .call(drawDateRangeControls, store, siteno, getSelectedDateRange(state), getSelectedCustomDateRange(state));
+        .call(drawTimeSpanControls, store, siteno);
     container.append('div')
         .attr('id', 'download-graph-data-container')
         .attr('hidden', true)
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/time-span-controls.js b/assets/src/scripts/monitoring-location/components/hydrograph/time-span-controls.js
index 8e6e85fa9..4ff84b0c8 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/time-span-controls.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/time-span-controls.js
@@ -5,10 +5,10 @@ import {datePicker, dateRangePicker} from 'uswds-components';
 
 import config from 'ui/config';
 
-import {getInputsForRetrieval} from 'ml/selectors/hydrograph-state-selector';
+import {getInputsForRetrieval, getSelectedTimeSpan} from 'ml/selectors/hydrograph-state-selector';
 
 import {retrieveHydrographData} from 'ml/store/hydrograph-data';
-import {clearGraphBrushOffset, setSelectedDateRange, setSelectedCustomDateRange} from 'ml/store/hydrograph-state';
+import {clearGraphBrushOffset, setSelectedTimeSpan} from 'ml/store/hydrograph-state';
 
 import {showDataIndicators} from './data-indicator';
 
@@ -106,29 +106,25 @@ const drawCustomDaysBeforeForm = function(container, store, siteno, initialDateR
  * @param {String} label - label for the date picker
  * @param {String} initialDate - ISO 8601 Date format
  */
-const drawDatePicker = function(container, id, label, initialDate) {
-    container.append('label')
-        .attr('class', 'usa-label')
-        .attr('id', `${id}-label`)
-        .attr('for', id)
-        .text(label);
-
-    container.append('div')
+const drawDatePicker = function(container, id, ariaLabel, initialDate) {
+    const formGroup = container.append('div')
+        .attr('class', 'usa-form-group');
+    formGroup.append('div')
         .attr('class', 'usa-hint')
         .attr('id', `${id}-hint`)
-        .text('mm/dd/yyyy')
-        .append('div')
-            .attr('class', 'usa-date-picker')
-            .attr('data-default-value', initialDate)
-            .attr('data-min-date', '1900-01-01')
-            .attr('data-max-date', '2100-12-31')
-            .append('input')
-                .attr('class', 'usa-input')
-                .attr('type', 'text')
-                .attr('id', id)
-                .attr('name', id)
-                .attr('aria-describedby', `${id}-label ${id}-hint`)
-                .attr('type', 'text');
+        .text('mm/dd/yyyy');
+    formGroup.append('div')
+        .attr('class', 'usa-date-picker time-span-date-range-picker')
+        .attr('data-default-value', initialDate ? initialDate : null)
+        .append('input')
+            .attr('class', 'usa-input')
+            .attr('type', 'text')
+            .attr('maxlength', 10)
+            .attr('size', 10)
+            .attr('id', id)
+            .attr('name', id)
+            .attr('aria-label', ariaLabel)
+            .attr('aria-describedby', `${id}-hint`);
 };
 
 /*
@@ -136,112 +132,27 @@ const drawDatePicker = function(container, id, label, initialDate) {
  * @param {D3 selection} container
  * @param {Redux store} store
  * @param {String} siteno
- * @param {String} initialDateRange - if 'custom' then this container is made visible
- * @param {Object} initialCustomDateRange - has start and end String properties containing an ISO 8601 date string
- *      and is used to set the initial values of the calendar pickers
  */
-const drawDateRangeForm = function(container, store, siteno) {
-    const calendarDaysContainer = container.append('div')
-        .attr('id', 'ts-customdaterange-select-container')
-        .attr('role', 'customdate')
-        .attr('class', 'usa-form')
-        .attr('aria-label', 'Custom date specification')
-        .attr('hidden', !isCustomPeriod(initialDateRange) ? null : true);
-    
-    const dateRangePickerContainer = calendarDaysContainer.append('div')
-        .attr('class', 'usa-date-range-picker');
-    dateRangePickerContainer.append('div')
-        .attr('id', 'start-date-form-group')
-        .attr('class', 'usa-form-group')
-        .call(drawDatePicker, 'custom-start-date', 'Start Date', initialCustomDateRange ? initialCustomDateRange.start : '');
-    dateRangePickerContainer.append('div')
-        .attr('id', 'end-date-form-group')
-        .attr('class', 'usa-form-group')
-        .call(drawDatePicker, 'custom-end-date', 'End Date', initialCustomDateRange ? initialCustomDateRange.end : '');
-
-    const calendarDaysValidationContainer = calendarDaysContainer.append('div')
-        .attr('class', 'usa-alert usa-alert--warning usa-alert--validation')
-        .attr('id', 'custom-date-alert-container')
-        .attr('hidden', true);
-    const dateAlertBody = calendarDaysValidationContainer.append('div')
-        .attr('class', 'usa-alert__body')
-        .attr('id', 'custom-date-alert');
-    dateAlertBody.append('h3')
-        .attr('class', 'usa-alert__heading')
-        .text('Date requirements');
+const drawDateRangeForm = function(container, store) {
+    const initialTimeSpan = getSelectedTimeSpan(store.getState());
+    const hasInitialDateRange = !isISODuration(initialTimeSpan);
+    container.append('span')
+        .text('Date range:');
+    const dateRangePickerContainer = container.append('div')
+        .attr('class', 'usa-date-range-picker')
+        .call(drawDatePicker, 'start-date', 'Start date', hasInitialDateRange ? initialTimeSpan.start : '');
+    dateRangePickerContainer.append('span').text('to');
+    dateRangePickerContainer.call(drawDatePicker, 'end-date', 'End date', hasInitialDateRange ? initialTimeSpan.end : '');
 
     // required to init the USWDS date picker after page load before calling the
     // dateRangePicker on function
     datePicker.init(dateRangePickerContainer.node());
     // required to init the USWDS date range picker after page load
     dateRangePicker.on(dateRangePickerContainer.node());
-
-    // Adds controls for the calendar day submit button
-    const calendarDaysSubmitContainer = calendarDaysContainer.append('div')
-        .attr('class', 'submit-button');
-    calendarDaysSubmitContainer.append('button')
-        .attr('class', 'usa-button')
-        .attr('id', 'custom-date-submit-calendar')
-        .attr('ga-on', 'click')
-        .attr('ga-event-category', 'TimeSeriesGraph')
-        .attr('ga-event-action', 'customDateSubmit')
-        .text('Display data on graph')
-        .on('click', function() {
-            const startDateStr = calendarDaysContainer.select('#custom-start-date').property('value');
-            const endDateStr = calendarDaysContainer.select('#custom-end-date').property('value');
-            if (startDateStr.length === 0 || endDateStr.length === 0) {
-                dateAlertBody.selectAll('p').remove();
-                dateAlertBody.append('p')
-                    .text('Both start and end dates must be specified.');
-                calendarDaysValidationContainer.attr('hidden', null);
-            } else {
-                const startTime = DateTime.fromFormat(startDateStr, 'LL/dd/yyyy', {zone: config.locationTimeZone}).toMillis();
-                const endTime = DateTime.fromFormat(endDateStr, 'LL/dd/yyyy', {zone: config.locationTimeZone}).toMillis();
-                if (startTime > endTime) {
-                    dateAlertBody.selectAll('p').remove();
-                    dateAlertBody.append('p')
-                        .text('The start date must precede the end date.');
-                    calendarDaysValidationContainer.attr('hidden', null);
-                } else {
-                    calendarDaysValidationContainer.attr('hidden', true);
-                    store.dispatch(clearGraphBrushOffset());
-                    store.dispatch(setSelectedCustomDateRange(DateTime.fromMillis(startTime, {zone: config.locationTimeZone}).toISODate(),
-                        DateTime.fromMillis(endTime, {zone: config.locationTimeZone}).toISODate()));
-                    store.dispatch(setSelectedDateRange('custom'));
-                    showDataIndicators(true, store);
-                    store.dispatch(retrieveHydrographData(siteno, getInputsForRetrieval(store.getState())))
-                        .then(() => {
-                            showDataIndicators(false, store);
-                        });
-                }
-            }
-        });
 };
 
-/*
- * Renders the date controls used to set the time range. This can be done by using short cut selections or by
- * using the custom date range form.
- * @param {D3 selection} elem
- * @param {Redux store} store
- * @param {String} siteno
- * @param {String} initialDateRange
- * @param {Object} initialCustomDateRange - has string start and end components containing an ISO 8601 date string
- */
-export const drawDateRangeControls = function(elem, store, siteno, initialDateRange, initialCustomDateRange) {
-    // Add a container that holds the custom selection radio buttons and the form fields
-    elem.append('div')
-        .attr('id', 'ts-daterange-select-container')
-        .attr('role', 'radiogroup')
-        .attr('aria-label', 'Time interval select')
-        .call(drawSelectRadioButtons, store, siteno, initialDateRange);
-    elem.append('div')
-        .attr('id', 'container-radio-group-and-form-buttons')
-        .call(drawCustomRadioButtons, initialDateRange)
-        .call(drawCustomDaysBeforeForm, store, siteno, initialDateRange)
-        .call(drawCustomCalendarDaysForm, store, siteno, initialDateRange, initialCustomDateRange);
-    setCustomFormVisibility(showCustomContainer(initialDateRange));
-};
-
-export const drawTimeSpanControl = function(container, store, siteno) {
-
+export const drawTimeSpanControls = function(container, store, siteno) {
+    container.append('div')
+        .attr('class', 'date-range-container')
+        .call(drawDateRangeForm, store);
 };
diff --git a/assets/src/scripts/monitoring-location/url-params.js b/assets/src/scripts/monitoring-location/url-params.js
index fb40985fd..eecf95ae5 100644
--- a/assets/src/scripts/monitoring-location/url-params.js
+++ b/assets/src/scripts/monitoring-location/url-params.js
@@ -1,8 +1,9 @@
 import {createStructuredSelector} from 'reselect';
 
 import {listen} from 'ui/lib/d3-redux';
+
 import {getPrimaryMethods} from 'ml/selectors/hydrograph-data-selector';
-import {isCompareIVDataVisible, getSelectedIVMethodID, getSelectedDateRange, getSelectedCustomDateRange,
+import {isCompareIVDataVisible, getSelectedIVMethodID, getSelectedTimeSpan,
     getSelectedParameterCode
 } from 'ml/selectors/hydrograph-state-selector';
 
@@ -21,23 +22,11 @@ export const renderTimeSeriesUrlParams = function(store) {
         methodId: getSelectedIVMethodID,
         methods: getPrimaryMethods,
         compare: isCompareIVDataVisible,
-        currentDateRange: getSelectedDateRange,
-        customDateRange: getSelectedCustomDateRange
-    }), ({parameterCode, methodId, methods, compare, currentDateRange, customDateRange}) => {
+        timeSpan: getSelectedTimeSpan
+    }), ({parameterCode, methodId, methods, compare, timeSpan}) => {
+        const timeSpanIsDuration = typeof timeSpan === 'string';
         let params = new window.URLSearchParams();
 
-        /* filter the 'currentDateRange', which comes in one of two forms
-        * 'P{some number}{Day or Year code}' (like P30D or P1Y) or the word 'custom'.
-        * In this case, 'custom' is a selection not using the 'period query', such as start and end date calendar dates.
-        * If the user selection is the default of 'P7D' or of the type 'custom', we will leave it as is.
-        * Otherwise, we will filter the code so it is generic and in the form of 'P'
-        * so that it will work for any arbitrary number of days in query parameters such as P20D.
-        */
-        const filteredCurrentDateRange =
-            currentDateRange === 'P7D' ? 'P7D' :
-            currentDateRange === 'custom' ? currentDateRange :
-            currentDateRange !== null ? currentDateRange.substr(0, 1) : '';
-
         if (parameterCode) {
             params.set('parameterCode', parameterCode);
         }
@@ -45,17 +34,13 @@ export const renderTimeSeriesUrlParams = function(store) {
         if (Object.keys(methods).length > 1) {
             params.set('timeSeriesId', methodId);
         }
-
-        switch(filteredCurrentDateRange) {
-            case 'P7D':
-                break;
-            case  'P':
-                params.set('period', currentDateRange);
-                break;
-            case 'custom':
-                params.set('startDT', customDateRange.start);
-                params.set('endDT', customDateRange.end);
+        if (timeSpanIsDuration) {
+            params.set('period', timeSpan);
+        } else {
+            params.set('startDT', timeSpan.start);
+            params.set('endDT', timeSpan.end);
         }
+
         if (compare) {
             params.set('compare', true);
         }
diff --git a/assets/src/styles/components/hydrograph/_app.scss b/assets/src/styles/components/hydrograph/_app.scss
index 4651f8bc5..e575fbfc1 100644
--- a/assets/src/styles/components/hydrograph/_app.scss
+++ b/assets/src/styles/components/hydrograph/_app.scss
@@ -3,58 +3,26 @@
  */
 @import './variables';
 
-#ts-daterange-select-container {
-  ul {
-    display: inline-block;
-    li {
-      display: inline-block;
-      label {
-        margin: 0;
-        @include u-margin-bottom(1);
-        width: 110px;
-      }
-    }
-  }
+#ts-method-select-container {
+  @include u-margin-bottom(2);
 }
-#container-radio-group-and-form-buttons {
-  border: solid 1px color('black');
-  @include u-margin-bottom(1);
-  #ts-custom-date-radio-group {
-    @include u-padding-left(1);
-    p {
-      @include u-text('bold');
-      @include u-margin-bottom(1);
-    }
-    ul {
+
+#change-time-span-container {
+  @include u-padding(2);
+  .date-range-container {
+    div {
       display: inline-block;
-      li {
-        display: inline-block;
-        @include u-padding-right(1);
-      }
     }
-  }
-  #ts-custom-days-before-today-select-container {
-    @include u-padding-right(1);
-    @include u-padding-bottom(1);
-    @include u-padding-left(1);
-    .usa-label {
-      @include u-text('bold');
+    .usa-date-picker {
+      width: 11rem;
     }
-  }
-  #ts-customdaterange-select-container {
-    @include u-padding-right(1);
-    @include u-padding-bottom(1);
-    @include u-padding-left(1);
-    label {
-      @include u-text('bold');
+
+    .usa-date-picker__calendar {
+      width: units($theme-input-max-width);
     }
   }
 }
 
-#ts-method-select-container {
-  @include u-margin-bottom(2);
-}
-
 #download-graph-data-container > div {
   @include grid-row;
   @include grid-gap;
-- 
GitLab