From 18d9d3ee056e531ad2a91913ad1340da6579c512 Mon Sep 17 00:00:00 2001
From: mbucknell <mbucknell@usgs.gov>
Date: Fri, 26 Feb 2021 08:43:01 -0600
Subject: [PATCH] Made the compare toggle disabled when custom date range or
 period. Fixed some issues that occurred while switching time periods that
 cause the primary parameter to be null. Also eliminated an needed service
 calls when that data is not requested or not available.

---
 assets/src/scripts/config.js                  |   4 +-
 .../components/hydrograph/graph-controls.js   |  16 ++-
 .../hydrograph/graph-controls.test.js         |  29 ++++-
 .../components/hydrograph/selectors/domain.js |  12 +-
 .../hydrograph/selectors/domain.test.js       |  45 ++++++-
 .../store/hydrograph-data.js                  |   2 +-
 .../store/hydrograph-data.test.js             |  51 ++++++++
 .../store/hydrograph-parameters.js            | 119 ++++++++++--------
 .../store/hydrograph-parameters.test.js       |  30 ++++-
 9 files changed, 240 insertions(+), 68 deletions(-)

diff --git a/assets/src/scripts/config.js b/assets/src/scripts/config.js
index 322d32030..d176d33a8 100644
--- a/assets/src/scripts/config.js
+++ b/assets/src/scripts/config.js
@@ -2,7 +2,7 @@
  * Export runtime configuration settings stored in the global CONFIG variable.
  */
 export default {
-    ...(window.CONFIG || {}),
+    ...window.CONFIG || {},
 
     // These are the screen size breakpoints in the USWDS style sheet
     USWDS_SMALL_SCREEN: 481,
@@ -13,6 +13,8 @@ export default {
     // Indicates the number digits that we allow users to enter when selecting a custom 'days before today' time span.
     MAX_DIGITS_FOR_DAYS_FROM_TODAY: 5,
 
+    ALLOW_COMPARE_DATA_FOR_PERIODS: ['P7D', 'P30D', 'P365D'],
+
     // Indicate a NWIS 'variable' has been modified in the application, such as a conversion from Celsius to Fahrenheit
     CALCULATED_TEMPERATURE_VARIABLE_CODE: 'F',
 
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 e5512d56e..98e51303c 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
@@ -1,7 +1,9 @@
+import {createStructuredSelector} from 'reselect';
 
+import config from 'ui/config';
 import {link} from 'ui/lib/d3-redux';
 
-import {getSelectedParameterCode} from 'ml/selectors/hydrograph-state-selector';
+import {getSelectedParameterCode, getSelectedDateRange} from 'ml/selectors/hydrograph-state-selector';
 import {getTimeRange} from 'ml/selectors/hydrograph-data-selector';
 
 import {retrieveMedianStatistics, retrievePriorYearIVData} from 'ml/store/hydrograph-data';
@@ -43,9 +45,15 @@ export const drawGraphControls = function(elem, store, siteno) {
             }
         })
         // Sets the state of the toggle
-        .call(link(store,function(elem, checked) {
-            elem.property('checked', checked);
-        }, isVisible('compare')));
+        .call(link(store,function(elem, {checked, selectedDateRange}) {
+            elem.property('checked', checked)
+                .attr('disabled',
+                selectedDateRange !== 'custom' && config.ALLOW_COMPARE_DATA_FOR_PERIODS.includes(selectedDateRange) ?
+                null : true);
+        }, createStructuredSelector({
+            checked: isVisible('compare'),
+            selectedDateRange: getSelectedDateRange
+        })));
     compareControlDiv.append('label')
         .classed('usa-checkbox__label', true)
         .attr('id', 'last-year-label')
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.test.js b/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.test.js
index a3342b13e..3223bff04 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.test.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.test.js
@@ -6,6 +6,7 @@ import * as hydrographData from 'ml/store/hydrograph-data';
 
 import {drawGraphControls} from './graph-controls';
 import {TEST_CURRENT_TIME_RANGE} from './mock-hydrograph-state';
+import {setSelectedDateRange} from "../../store/hydrograph-state";
 
 // Tests for the graph-controls module
 describe('monitoring-location/components/hydrograph/graph-controls', () => {
@@ -24,6 +25,7 @@ describe('monitoring-location/components/hydrograph/graph-controls', () => {
                 },
                 hydrographState: {
                     showCompareIVData: false,
+                    selectedDateRange: 'P7D',
                     showMedianData: false,
                     selectedParameterCode: '72019'
                 }
@@ -41,10 +43,11 @@ describe('monitoring-location/components/hydrograph/graph-controls', () => {
         });
 
         // last year checkbox tests
-        it('Should render the compare toggle unchecked', () => {
+        it('Should render the compare toggle unchecked and not disabled', () => {
             const checkbox = select('#last-year-checkbox');
             expect(checkbox.size()).toBe(1);
             expect(checkbox.property('checked')).toBe(false);
+            expect(checkbox.attr('disabled')).toBeNull();
         });
 
         it('Should set the compare visibility to true and retrieve the Prior year data', () => {
@@ -71,6 +74,30 @@ describe('monitoring-location/components/hydrograph/graph-controls', () => {
             expect(retrievePriorYearSpy.mock.calls).toHaveLength(1);
         });
 
+        it('Should change the checkbox to disabled if the selectedDateRange is set to custom', () => {
+            store.dispatch(setSelectedDateRange('custom'));
+            return new Promise(resolve => {
+                window.requestAnimationFrame(() => {
+                    const checkbox = select('#last-year-checkbox');
+                    expect(checkbox.attr('disabled')).toBe('true');
+
+                    resolve();
+                });
+            });
+        });
+
+        it('Should change the checkbox to disabled if the selectedDateRange is a custom period', () => {
+            store.dispatch(setSelectedDateRange('P45D'));
+            return new Promise(resolve => {
+                window.requestAnimationFrame(() => {
+                    const checkbox = select('#last-year-checkbox');
+                    expect(checkbox.attr('disabled')).toBe('true');
+
+                    resolve();
+                });
+            });
+        });
+
         //median visibility tests
         it('Should render the median toggle unchecked', () => {
             const checkbox = select('#median-checkbox');
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js
index 9c512e977..f051e17c7 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js
@@ -24,6 +24,10 @@ export const SYMLOG_PARMS = [
     '72137'
 ];
 
+const useSymlog = function(parameter) {
+    return parameter ? SYMLOG_PARMS.indexOf(parameter.parameterCode) > -1 : false;
+};
+
 /*
  * The helper functions are exported as an aid to testing. Only the selectors are actually imported into other modules
  */
@@ -137,7 +141,7 @@ export const getRoundedTickValues = function(additionalTickValues, yDomain) {
 /**
  *  Helper function that, when negative values are present on the y-axis, adds additional negative values to the set of
  *  tick values used to fill tick mark value gaps on the y-axis on some log scale graphs
- * @param {array} additionalTickValues, a set of tick mark values
+ * @param {array} tickValues, a set of tick mark values
  * @returns {array} additionalTickValues, a set of tick mark values with (when needed) additional negative values
  */
 export const generateNegativeTicks = function(tickValues, additionalTickValues) {
@@ -201,7 +205,7 @@ export const getPrimaryValueRange = createSelector(
             result = [Math.min(...valueExtent), Math.max(...valueExtent)];
 
             // Add padding to the extent and handle empty data sets.
-            result = extendDomain(result, SYMLOG_PARMS.indexOf(parameter.parameterCode) !== -1);
+            result = extendDomain(result, useSymlog(parameter));
         }
         return result;
     }
@@ -216,14 +220,12 @@ export const getYTickDetails = createSelector(
     getPrimaryValueRange,
     getPrimaryParameter,
     (yDomain, parameter) => {
-        const isSymlog = SYMLOG_PARMS.indexOf(parameter.parameterCode) > -1;
-
         let tickValues = ticks(yDomain[0], yDomain[1], Y_TICK_COUNT);
 
         // When there are too many log scale ticks they will overlap--reduce the number in proportion to the number of ticks
         // For example, if there are 37 tick marks, every 4 ticks will be used... if there are 31 tick marks, every 3 ticks
         // will be used. Screens smaller than the USWDS defined medium screen will use fewer tick marks than larger screens.
-        if (isSymlog) {
+        if (useSymlog(parameter)) {
             // add additional ticks and labels to log scales as needed
             tickValues = getFullArrayOfAdditionalTickMarks(tickValues, yDomain);
             // remove ticks if there are too many of them
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.test.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.test.js
index aa30dce29..c8e9928c6 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.test.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.test.js
@@ -1,10 +1,17 @@
+import {
+    TEST_PRIMARY_IV_DATA,
+    TEST_GW_LEVELS,
+    TEST_MEDIAN_DATA,
+    TEST_CURRENT_TIME_RANGE
+} from '../mock-hydrograph-state';
 import {
     extendDomain,
     getYTickDetails,
     getFullArrayOfAdditionalTickMarks,
     getLowestAbsoluteValueOfTickValues,
     getRoundedTickValues,
-    generateNegativeTicks
+    generateNegativeTicks,
+    getPrimaryValueRange
 } from './domain';
 
 
@@ -58,6 +65,13 @@ describe('monitoring-location/components/hydrograph/selectors/domain module', ()
     });
 
     describe('getYTickDetails', () => {
+        it('Returns the default tick details if no parameter is defined', () => {
+            const tickDetails = getYTickDetails.resultFunc([0, 1], null);
+            expect(tickDetails.tickValues).toEqual(expect.any(Array));
+            expect(tickDetails.tickFormat).toEqual(expect.any(Function));
+            expect(tickDetails.tickFormat(1)).toEqual(expect.any(String));
+        });
+
         it('returns ticks and a formatting function', () => {
             const tickDetails = getYTickDetails.resultFunc([0, 1], {parameterCode: '00065'});
             expect(tickDetails.tickValues).toEqual(expect.any(Array));
@@ -120,4 +134,33 @@ describe('monitoring-location/components/hydrograph/selectors/domain module', ()
             expect(generateNegativeTicks(testTickValues_2, additionalTickValues)).toEqual(expectedReturnedArrayWithNegatives);
         });
     });
+
+    describe('getPrimaryValueRange', () => {
+        it('if no hydrograph data then return a range of [0, 1]', () => {
+            expect(getPrimaryValueRange({
+                hydrographData: {},
+                hydrographState: {
+                    showCompareIVData: true,
+                    showMedianData: true
+                }
+            })).toEqual([0, 1]);
+        });
+
+        it('Show range if hydrograph data exists', () => {
+            const result = getPrimaryValueRange({
+                hydrographData: {
+                    currentTimeRange: TEST_CURRENT_TIME_RANGE,
+                    primaryIVData: TEST_PRIMARY_IV_DATA,
+                    groundwaterLevels: TEST_GW_LEVELS,
+                    medianStatisticsData: TEST_MEDIAN_DATA
+                },
+                hydrographState: {
+                    showCompareIVData: true,
+                    showMedianData: true
+                }
+            });
+            expect(result[0]).toBeLessThan(15.9);
+            expect(result[1]).toBeGreaterThan(27.2);
+        });
+    });
 });
diff --git a/assets/src/scripts/monitoring-location/store/hydrograph-data.js b/assets/src/scripts/monitoring-location/store/hydrograph-data.js
index 1e4040ef0..216721a1d 100644
--- a/assets/src/scripts/monitoring-location/store/hydrograph-data.js
+++ b/assets/src/scripts/monitoring-location/store/hydrograph-data.js
@@ -301,7 +301,7 @@ export const retrieveHydrographData = function(siteno, {parameterCode, period, s
             fetchPromises.push(dispatch(
                 retrieveGroundwaterLevels(siteno, {parameterCode, period, startTime, endTime})));
         }
-        if (hasIVData && loadCompare) {
+        if (hasIVData && loadCompare && config.ALLOW_COMPARE_DATA_FOR_PERIODS.includes(period)) {
             fetchPromises.push(dispatch(
                 retrievePriorYearIVData(siteno, {
                     parameterCode: parameterCode,
diff --git a/assets/src/scripts/monitoring-location/store/hydrograph-data.test.js b/assets/src/scripts/monitoring-location/store/hydrograph-data.test.js
index 7f58cf4c6..8f01ffef0 100644
--- a/assets/src/scripts/monitoring-location/store/hydrograph-data.test.js
+++ b/assets/src/scripts/monitoring-location/store/hydrograph-data.test.js
@@ -207,6 +207,57 @@ describe('monitoring-location/store/hydrograph-data', () => {
                 });
                 expect(mockStatsCalls).toHaveLength(0);
             });
+
+            it('Loads compare data if requested and period is not a custom period', () => {
+                config.ivPeriodOfRecord = {
+                    '00060': {begin_date: '2010-01-01', end_date: '2020-01-01'}
+                };
+                config.gwPeriodOfRecord = {};
+                store.dispatch(retrieveHydrographData('11112222', {
+                    parameterCode: '00060',
+                    period: 'P7D',
+                    startTime: null,
+                    endTime: null,
+                    loadCompare: true,
+                    loadMedian: true
+                }));
+
+                expect(ivDataService.fetchTimeSeries.mock.calls).toHaveLength(2);
+            });
+
+            it('Does not load compare data if requested and period is a custom period', () => {
+                config.ivPeriodOfRecord = {
+                    '00060': {begin_date: '2010-01-01', end_date: '2020-01-01'}
+                };
+                config.gwPeriodOfRecord = {};
+                store.dispatch(retrieveHydrographData('11112222', {
+                    parameterCode: '00060',
+                    period: 'P10D',
+                    startTime: null,
+                    endTime: null,
+                    loadCompare: true,
+                    loadMedian: true
+                }));
+
+                expect(ivDataService.fetchTimeSeries.mock.calls).toHaveLength(1);
+            });
+
+            it('Does not load compare data if requested and using custom start and end time', () => {
+                config.ivPeriodOfRecord = {
+                    '00060': {begin_date: '2010-01-01', end_date: '2020-01-01'}
+                };
+                config.gwPeriodOfRecord = {};
+                store.dispatch(retrieveHydrographData('11112222', {
+                    parameterCode: '00060',
+                    period: null,
+                    startTime: '2020-01-01',
+                    endTime: '2020-01-31',
+                    loadCompare: true,
+                    loadMedian: true
+                }));
+
+                expect(ivDataService.fetchTimeSeries.mock.calls).toHaveLength(1);
+            });
         });
 
         describe('data is loaded into the Redux store', () => {
diff --git a/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js b/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js
index 50ad00b1e..f5e70358b 100644
--- a/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js
+++ b/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js
@@ -26,61 +26,72 @@ const updateHydrographParameters = function(parameters) {
  */
 export const retrieveHydrographParameters = function(siteno) {
     return function(dispatch) {
-        const fetchIVParameters = fetchTimeSeries({sites: [siteno]})
-            .then(series => {
-                if (series.value && series.value.timeSeries) {
-                    const allParameterCodes = series.value.timeSeries.map(ts => ts.variable.variableCode[0].value);
-                    return series.value.timeSeries.reduce((varsByPCode, ts) => {
-                        const parameterCode = ts.variable.variableCode[0].value;
-                        varsByPCode[parameterCode] = {
-                            parameterCode: parameterCode,
-                            name: ts.variable.variableName,
-                            description: ts.variable.variableDescription,
-                            unit: ts.variable.unit.unitCode,
-                            hasIVData: true
-                        };
-                        // If it is a celsius parameterCode, add a variable for calculated Fahrenheit.
-                        if (config.TEMPERATURE_PARAMETERS.celsius.includes(parameterCode) &&
-                            !hasMeasuredFahrenheitParameter(parameterCode, allParameterCodes)) {
-                            const fahrenheitParameter = getConvertedTemperatureParameter(varsByPCode[parameterCode]);
-                            varsByPCode[fahrenheitParameter.parameterCode] = fahrenheitParameter;
-                        }
-                        return varsByPCode;
-                    }, {});
-                } else {
-                    return null;
-                }
-            })
-            .catch(reason => {
-                console.error(reason);
-                throw reason;
-            });
-        const fetchGWLevelParameters = fetchGroundwaterLevels({site: siteno})
-            .then(series => {
-                if (series.value && series.value.timeSeries) {
-                    return series.value.timeSeries.reduce((varsByPCode, ts) => {
-                        const parameterCode = ts.variable.variableCode[0].value;
-                        varsByPCode[parameterCode] = {
-                            parameterCode: parameterCode,
-                            name: ts.variable.variableName,
-                            description: ts.variable.variableDescription,
-                            unit: ts.variable.unit.unitCode,
-                            hasGWLevelsData: true
-                        };
-                        return varsByPCode;
-                    }, {});
-                } else {
-                    return null;
-                }
-            })
-            .catch(reason => {
-                console.error(reason);
-                throw reason;
+        let fetchPromises = [];
+        if (config.ivPeriodOfRecord) {
+            const fetchIVParameters = fetchTimeSeries({sites: [siteno]})
+                .then(series => {
+                    if (series.value && series.value.timeSeries) {
+                        const allParameterCodes = series.value.timeSeries.map(ts => ts.variable.variableCode[0].value);
+                        return series.value.timeSeries.reduce((varsByPCode, ts) => {
+                            const parameterCode = ts.variable.variableCode[0].value;
+                            varsByPCode[parameterCode] = {
+                                parameterCode: parameterCode,
+                                name: ts.variable.variableName,
+                                description: ts.variable.variableDescription,
+                                unit: ts.variable.unit.unitCode,
+                                hasIVData: true
+                            };
+                            // If it is a celsius parameterCode, add a variable for calculated Fahrenheit.
+                            if (config.TEMPERATURE_PARAMETERS.celsius.includes(parameterCode) &&
+                                !hasMeasuredFahrenheitParameter(parameterCode, allParameterCodes)) {
+                                const fahrenheitParameter = getConvertedTemperatureParameter(varsByPCode[parameterCode]);
+                                varsByPCode[fahrenheitParameter.parameterCode] = fahrenheitParameter;
+                            }
+                            return varsByPCode;
+                        }, {});
+                    } else {
+                        return null;
+                    }
+                })
+                .catch(reason => {
+                    console.error(reason);
+                    throw reason;
+                });
+            fetchPromises.push(fetchIVParameters);
+        }
+        if (config.gwPeriodOfRecord) {
+            const fetchGWLevelParameters = fetchGroundwaterLevels({site: siteno})
+                .then(series => {
+                    if (series.value && series.value.timeSeries) {
+                        return series.value.timeSeries.reduce((varsByPCode, ts) => {
+                            const parameterCode = ts.variable.variableCode[0].value;
+                            varsByPCode[parameterCode] = {
+                                parameterCode: parameterCode,
+                                name: ts.variable.variableName,
+                                description: ts.variable.variableDescription,
+                                unit: ts.variable.unit.unitCode,
+                                hasGWLevelsData: true
+                            };
+                            return varsByPCode;
+                        }, {});
+                    } else {
+                        return null;
+                    }
+                })
+                .catch(reason => {
+                    console.error(reason);
+                    throw reason;
+                });
+            fetchPromises.push(fetchGWLevelParameters);
+        }
+        if (fetchPromises.length) {
+            return Promise.all(fetchPromises).then(([ivVars, gwVars]) => {
+                const mergedVars = merge({}, gwVars, ivVars);
+                dispatch(updateHydrographParameters(mergedVars));
             });
-        return Promise.all([fetchIVParameters, fetchGWLevelParameters]).then(([ivVars, gwVars]) => {
-            const mergedVars = merge({}, gwVars, ivVars);
-            dispatch(updateHydrographParameters(mergedVars));
-        });
+        } else {
+            return Promise.resolve();
+        }
     };
 };
 
diff --git a/assets/src/scripts/monitoring-location/store/hydrograph-parameters.test.js b/assets/src/scripts/monitoring-location/store/hydrograph-parameters.test.js
index c0abb83d7..d3a15c696 100644
--- a/assets/src/scripts/monitoring-location/store/hydrograph-parameters.test.js
+++ b/assets/src/scripts/monitoring-location/store/hydrograph-parameters.test.js
@@ -3,13 +3,13 @@ import {applyMiddleware, combineReducers, createStore} from 'redux';
 import {default as thunk} from 'redux-thunk';
 import sinon from 'sinon';
 
+import config from 'ui/config';
 import {MOCK_LATEST_IV_DATA, MOCK_LATEST_GW_DATA} from 'ui/mock-service-data';
 
 import * as ivDataService from 'ui/web-services/instantaneous-values';
 import * as groundwaterLevelService from 'ui/web-services/groundwater-levels';
 
 import {hydrographParametersReducer, retrieveHydrographParameters} from './hydrograph-parameters';
-import {hydrographDataReducer} from "./hydrograph-data";
 
 describe('monitoring-location/store/hydrograph-parameters', () => {
     let store;
@@ -38,6 +38,12 @@ describe('monitoring-location/store/hydrograph-parameters', () => {
     describe('retrieveHydrographParameters', () => {
 
         beforeEach(() => {
+            config.ivPeriodOfRecord = {
+                '00060': {begin_date: '2010-01-01', end_date: '2020-01-01'}
+            };
+            config.gwPeriodOfRecord = {
+                '00060': {begin_date: '2010-01-01', end_date: '2020-01-01'}
+            };
             ivDataService.fetchTimeSeries =
                 jest.fn().mockReturnValue(Promise.resolve(JSON.parse(MOCK_LATEST_IV_DATA)));
             groundwaterLevelService.fetchGroundwaterLevels =
@@ -60,6 +66,28 @@ describe('monitoring-location/store/hydrograph-parameters', () => {
             });
         });
 
+        it('Expects only IV data to be fetched if no period of record for gw', () => {
+            config.gwPeriodOfRecord = null;
+            store.dispatch(retrieveHydrographParameters('11112222'));
+
+            const mockIVCalls = ivDataService.fetchTimeSeries.mock.calls;
+            const mockGWCalls = groundwaterLevelService.fetchGroundwaterLevels.mock.calls;
+
+            expect(mockIVCalls).toHaveLength(1);
+            expect(mockGWCalls).toHaveLength(0);
+        });
+
+        it('Expects only Gw data to be fetched if no period of record for iv', () => {
+            config.ivPeriodOfRecord = null;
+            store.dispatch(retrieveHydrographParameters('11112222'));
+
+            const mockIVCalls = ivDataService.fetchTimeSeries.mock.calls;
+            const mockGWCalls = groundwaterLevelService.fetchGroundwaterLevels.mock.calls;
+
+            expect(mockIVCalls).toHaveLength(0);
+            expect(mockGWCalls).toHaveLength(1);
+        });
+
         it('Expects the Redux store to save the parameters from both IV and GW calls', () => {
             return store.dispatch(retrieveHydrographParameters('11112222')).then(() => {
                 const hydrographParameters = store.getState().hydrographParameters;
-- 
GitLab