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