From c2e2f92fb62051be2831c650b6c1769702c08dd9 Mon Sep 17 00:00:00 2001 From: mbucknell <mbucknell@usgs.gov> Date: Wed, 22 Dec 2021 13:46:29 -0600 Subject: [PATCH] Code working to switch to different shortcut defaults for SV only sites. --- .../hydrograph/days-before-shortcuts.js | 56 ++++++++++++++++++- .../components/hydrograph/download-data.js | 2 +- .../components/hydrograph/index.js | 18 ++---- .../hydrograph/selectors/parameter-data.js | 27 +-------- ...-data-utils.js => parameter-code-utils.js} | 38 +++++++++++++ ...s.test.js => parameter-code-utils.test.js} | 4 +- .../selectors/hydrograph-state-selector.js | 30 ++++++++-- .../store/hydrograph-data.js | 2 +- .../store/hydrograph-parameters.js | 2 +- assets/src/scripts/utils.js | 29 ---------- assets/src/scripts/utils.test.js | 20 +------ .../web-services/groundwater-levels.js | 5 +- .../styles/components/hydrograph/_app.scss | 15 ++--- .../templates/macros/components.html | 2 +- 14 files changed, 142 insertions(+), 108 deletions(-) rename assets/src/scripts/monitoring-location/{iv-data-utils.js => parameter-code-utils.js} (56%) rename assets/src/scripts/monitoring-location/{iv-data-utils.test.js => parameter-code-utils.test.js} (95%) diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/days-before-shortcuts.js b/assets/src/scripts/monitoring-location/components/hydrograph/days-before-shortcuts.js index d9805eb61..be49128f9 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/days-before-shortcuts.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/days-before-shortcuts.js @@ -1,18 +1,29 @@ +import config from 'ui/config.js'; + import {link} from 'ui/lib/d3-redux'; -import {getSelectedTimeSpan, getInputsForRetrieval} from 'ml/selectors/hydrograph-state-selector'; +import {getSelectedTimeSpan, getInputsForRetrieval, getSelectedParameterCode} from 'ml/selectors/hydrograph-state-selector'; import {clearGraphBrushOffset, setSelectedTimeSpan} from 'ml/store/hydrograph-state'; import {retrieveHydrographData} from 'ml/store/hydrograph-data'; import {showDataIndicators} from './data-indicator'; -const SHORTCUT_BUTTONS = [ +const IV_SHORTCUT_BUTTONS = [ {timeSpan: 'P7D', label: '7 days'}, {timeSpan: 'P30D', label: '30 days'}, {timeSpan: 'P365D', label: '1 year'} ]; +const IV_SHORTCUT_TIME_SPANS = IV_SHORTCUT_BUTTONS.map(shortcut => shortcut.timeSpan); + +const SV_SHORTCUT_BUTTONS = [ + {timeSpan: 'P1Y', label: '1 year'}, + {timeSpan: 'P10Y', label: '10 years'}, + {timeSpan: 'periodOfRecord', label: 'Period of record'} +]; +const SV_SHORTCUT_TIME_SPANS = SV_SHORTCUT_BUTTONS.map(shortcut => shortcut.timeSpan); + /* * Render a single short cut radio button. Set up the click event handler to update the @@ -50,6 +61,14 @@ const drawShortcutRadioButton = function(container, store, siteno, agencyCode, { }, getSelectedTimeSpan)); }; +const hasIVData = function(parameterCode) { + return config.ivPeriodOfRecord && parameterCode in config.ivPeriodOfRecord; +}; + +const hasGWData = function(parameterCode) { + return config.gwPeriodOfRecord && parameterCode in config.gwPeriodOfRecord; +}; + /* * Render the shortcut days before radio buttons on container. Set up the appropriate event handlers * for user actions and for changes to the selectedTimeSpan. @@ -58,7 +77,38 @@ const drawShortcutRadioButton = function(container, store, siteno, agencyCode, { * @param {String} siteno */ export const drawShortcutDaysBeforeButtons = function(container, store, siteno, agencyCode) { + const initialParameterCode = getSelectedParameterCode(store.getState()); const formContainer = container.append('div') .attr('class', 'usa-form'); - SHORTCUT_BUTTONS.forEach(shortcut => drawShortcutRadioButton(formContainer, store, siteno, agencyCode, shortcut)); + const ivButtonContainer = formContainer.append('div') + .attr('hidden', hasIVData(initialParameterCode) ? null : true); + const svButtonContainer = formContainer.append('div') + .attr('hidden', hasIVData(initialParameterCode) ? true : null); + + IV_SHORTCUT_BUTTONS.forEach(shortcut => + drawShortcutRadioButton(ivButtonContainer, store, siteno, agencyCode, shortcut)); + SV_SHORTCUT_BUTTONS.forEach(shortcut => + drawShortcutRadioButton(svButtonContainer, store, siteno, agencyCode, shortcut)); + formContainer.call(link(store, function(container, selectedParameterCode) { + const hasIVData = config.ivPeriodOfRecord && selectedParameterCode in config.ivPeriodOfRecord; + const hasGWData = config.gwPeriodOfRecord && selectedParameterCode in config.gwPeriodOfRecord; + const selectedTimeSpan = getSelectedTimeSpan(store.getState()); + if (ivButtonContainer.attr('hidden') && hasIVData) { + ivButtonContainer.attr('hidden', null); + svButtonContainer.attr('hidden', true); + if (typeof selectedTimeSpan === 'string' && SV_SHORTCUT_TIME_SPANS.includes(selectedTimeSpan)) { + ivButtonContainer.select('input') + .property('checked', true) + .dispatch('click'); + } + } else if (svButtonContainer.attr('hidden') && !hasIVData && hasGWData) { + ivButtonContainer.attr('hidden', true); + svButtonContainer.attr('hidden', null); + if (typeof getSelectedTimeSpan(store.getState()) === 'string' && IV_SHORTCUT_TIME_SPANS.includes(selectedTimeSpan)) { + svButtonContainer.select('input') + .property('checked', true) + .dispatch('click'); + } + } + }, getSelectedParameterCode)); }; diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/download-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/download-data.js index 293ba454d..161a426f9 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/download-data.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/download-data.js @@ -13,7 +13,7 @@ import {getGroundwaterServiceURL} from 'ui/web-services/groundwater-levels'; import {drawErrorAlert} from 'd3render/alerts'; -import {isCalculatedTemperature} from 'ml/iv-data-utils'; +import {isCalculatedTemperature} from 'ml/parameter-code-utils'; import {getTimeRange, getPrimaryParameter} from 'ml/selectors/hydrograph-data-selector'; import {hasVisibleIVData, hasVisibleMedianStatisticsData, hasVisibleGroundwaterLevels} from './selectors/time-series-data'; diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/index.js b/assets/src/scripts/monitoring-location/components/hydrograph/index.js index c8573079c..11cf96374 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/index.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/index.js @@ -30,6 +30,7 @@ import {drawSelectActions} from './select-actions'; import {drawShortcutDaysBeforeButtons} from './days-before-shortcuts'; import {initializeTimeSeriesGraph, drawTimeSeriesGraphData} from './time-series-graph'; import {initializeTooltipCursorSlider, drawTooltipCursorSlider} from './tooltip'; +import {getInputsForRetrieval} from "../../selectors/hydrograph-state-selector"; /* * Renders the hydrograph on the node element using the Redux store for state information. The siteno, latitude, and @@ -63,11 +64,6 @@ export const attachToNode = function(store, return; } - const initialPeriod = startDT && endDT ? 'custom' : period || 'P7D'; - const initialStartTime = startDT ? - DateTime.fromISO(startDT, {zone: config.locationTimeZone}).toISO() : null; - const initialEndTime = endDT ? - DateTime.fromISO(endDT, {zone: config.locationTimeZone}).endOf('day').toISO() : null; const initialLoadCompare = compare === 'true' || compare === true ? true : false; const thisShowOnlyGraph = showOnlyGraph === 'true' || showOnlyGraph === true ? true : false; const thisShowMLName = showMLName === 'true' || showMLName === true ? true : false; @@ -87,14 +83,8 @@ export const attachToNode = function(store, } // Fetch all data needed to render the hydrograph - const fetchHydrographDataPromise = store.dispatch(retrieveHydrographData(siteno, agencyCd, { - parameterCode: parameterCode, - period: initialPeriod === 'custom' ? null : initialPeriod, - startTime: initialStartTime, - endTime: initialEndTime, - loadCompare: initialLoadCompare, - loadMedian: false - })); + const fetchHydrographDataPromise = store.dispatch(retrieveHydrographData(siteno, agencyCd, + getInputsForRetrieval(store.getState()))); // if showing the controls, fetch the parameter meta data if (!thisShowOnlyGraph) { @@ -113,7 +103,7 @@ export const attachToNode = function(store, // Render initial UI elements prior to completion of data fetching if (!showOnlyGraph) { - nodeElem.select('.short-cut-days-before-container').call(drawShortcutDaysBeforeButtons, store, siteno, agencyCd); + nodeElem.select('.short-cut-time-span-container').call(drawShortcutDaysBeforeButtons, store, siteno, agencyCd); select(node).select('.select-actions-container').call(drawSelectActions, store, siteno, agencyCd); } diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.js index 90bd21fa0..fbdc6de0d 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.js @@ -1,9 +1,10 @@ -import {DateTime} from 'luxon'; import {createSelector} from 'reselect'; import config from 'ui/config'; import {sortedParameters} from 'ui/utils'; +import {getPeriodOfRecord} from 'ml/parameter-code-utils'; + /** * Returns a Redux selector function which returns an sorted array of metadata * for each available parameter . Each object has the following properties: @@ -25,29 +26,7 @@ export const getAvailableParameters = createSelector( .map((parameter) => { const parameterCode = parameter.parameterCode; const measuredParameterCode = parameterCode.replace(config.CALCULATED_TEMPERATURE_VARIABLE_CODE, ''); - const ivPeriodOfRecord = parameter.hasIVData && measuredParameterCode in config.ivPeriodOfRecord ? - config.ivPeriodOfRecord[measuredParameterCode] : null; - const gwPeriodOfRecord = parameter.hasGWLevelsData && measuredParameterCode in config.gwPeriodOfRecord ? - config.gwPeriodOfRecord[measuredParameterCode] : null ; - let periodOfRecord; - - if (ivPeriodOfRecord && gwPeriodOfRecord) { - periodOfRecord = { - begin_date: DateTime.fromISO(ivPeriodOfRecord.begin_date) < DateTime.fromISO(gwPeriodOfRecord.begin_date) ? - ivPeriodOfRecord.begin_date : gwPeriodOfRecord.begin_date, - end_date: DateTime.fromISO(ivPeriodOfRecord.end_date) > DateTime.fromISO(gwPeriodOfRecord.end_date) ? - ivPeriodOfRecord.end_date : gwPeriodOfRecord.end_date - }; - } else if (ivPeriodOfRecord) { - periodOfRecord = ivPeriodOfRecord; - } else if (gwPeriodOfRecord) { - periodOfRecord = gwPeriodOfRecord; - } else { - periodOfRecord = { - begin_date: '', - end_date: '' - }; - } + const periodOfRecord = getPeriodOfRecord(parameterCode); const hasWaterAlert = !!(parameter.hasIVData && config.WATER_ALERT_PARAMETER_CODES.includes(measuredParameterCode)); let waterAlertDisplayText; diff --git a/assets/src/scripts/monitoring-location/iv-data-utils.js b/assets/src/scripts/monitoring-location/parameter-code-utils.js similarity index 56% rename from assets/src/scripts/monitoring-location/iv-data-utils.js rename to assets/src/scripts/monitoring-location/parameter-code-utils.js index 840980b28..ce12d9e9c 100644 --- a/assets/src/scripts/monitoring-location/iv-data-utils.js +++ b/assets/src/scripts/monitoring-location/parameter-code-utils.js @@ -1,4 +1,6 @@ +import {DateTime} from 'luxon'; + import config from 'ui/config'; /* @@ -55,3 +57,39 @@ export const getConvertedTemperatureParameter = function(parameter) { unit: parameter.unit.replace('C', 'F') }; }; + +/* + * Determines the period of record for parameterCode using the information in the config for IV and GW period of record + * @param {String} - parameterCode + * @return {Object} - has properties begin_date and end_date string properties that contain the ISO 8601 date + * string or if no period of record, then the properties will contain the null string. + */ +export const getPeriodOfRecord = function(parameterCode) { + const measuredParameterCode = parameterCode.replace(config.CALCULATED_TEMPERATURE_VARIABLE_CODE, ''); + const ivPeriodOfRecord = config.ivPeriodOfRecord && measuredParameterCode in config.ivPeriodOfRecord ? + config.ivPeriodOfRecord[measuredParameterCode] : null; + const gwPeriodOfRecord = config.gwPeriodOfRecord && measuredParameterCode in config.gwPeriodOfRecord ? + config.gwPeriodOfRecord[measuredParameterCode] : null; + + let periodOfRecord; + if (ivPeriodOfRecord && gwPeriodOfRecord) { + periodOfRecord = { + begin_date: DateTime.fromISO(ivPeriodOfRecord.begin_date) < DateTime.fromISO(gwPeriodOfRecord.begin_date) ? + ivPeriodOfRecord.begin_date : gwPeriodOfRecord.begin_date, + end_date: DateTime.fromISO(ivPeriodOfRecord.end_date) > DateTime.fromISO(gwPeriodOfRecord.end_date) ? + ivPeriodOfRecord.end_date : gwPeriodOfRecord.end_date + }; + } else if (ivPeriodOfRecord) { + periodOfRecord = ivPeriodOfRecord; + } else if (gwPeriodOfRecord) { + periodOfRecord = gwPeriodOfRecord; + } else { + periodOfRecord = { + begin_date: '', + end_date: '' + }; + } + return periodOfRecord; +}; + + diff --git a/assets/src/scripts/monitoring-location/iv-data-utils.test.js b/assets/src/scripts/monitoring-location/parameter-code-utils.test.js similarity index 95% rename from assets/src/scripts/monitoring-location/iv-data-utils.test.js rename to assets/src/scripts/monitoring-location/parameter-code-utils.test.js index 5db86539e..dc7df820c 100644 --- a/assets/src/scripts/monitoring-location/iv-data-utils.test.js +++ b/assets/src/scripts/monitoring-location/parameter-code-utils.test.js @@ -1,8 +1,8 @@ import {hasMeasuredFahrenheitParameter, isCalculatedTemperature, getConvertedTemperatureParameter -} from './iv-data-utils'; +} from './parameter-code-utils'; -describe('monitoring-location/iv-data-utils', () => { +describe('monitoring-location/parameter-code-utils', () => { describe('hasMeasuredFahrenheitParameter', () => { const allParameterCodes = ['72019', '00020', '00021', '00010', '45589']; 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 e5dd85023..e14cf1ccb 100644 --- a/assets/src/scripts/monitoring-location/selectors/hydrograph-state-selector.js +++ b/assets/src/scripts/monitoring-location/selectors/hydrograph-state-selector.js @@ -4,6 +4,8 @@ import {createSelector} from 'reselect'; import config from 'ui/config'; +import {getPeriodOfRecord} from 'ml/parameter-code-utils'; + /* * The following selector functions return a function which returns the selected data. */ @@ -29,11 +31,29 @@ export const getInputsForRetrieval = createSelector( isMedianDataVisible, (parameterCode, selectedTimeSpan, loadCompare, loadMedian) => { const timeSpanIsDuration = typeof selectedTimeSpan === 'string'; - const period = timeSpanIsDuration ? selectedTimeSpan : null; - const startTime = timeSpanIsDuration ? - null: DateTime.fromISO(selectedTimeSpan.start, {zone: config.locationTimeZone}).toISO(); - const endTime = timeSpanIsDuration ? - null: DateTime.fromISO(selectedTimeSpan.end, {zone: config.locationTimeZone}).endOf('day').toISO(); + const period = timeSpanIsDuration && selectedTimeSpan.endsWith('D') ? selectedTimeSpan : null; + let startTime = null; + let endTime = null; + if (!period) { + if (timeSpanIsDuration) { + const now = DateTime.now().setZone(config.locationTimeZone); + if (selectedTimeSpan.endsWith('Y')) { + const years = parseInt(selectedTimeSpan.slice(1, -1)); + startTime = now.minus({years: years}).startOf('day').toISO(); + endTime = now.toISO(); + } else { + // Assuming that the time span is the period of record for the parameter code + const periodOfRecord = getPeriodOfRecord(parameterCode); + startTime = periodOfRecord.begin_date ? + DateTime.fromISO(periodOfRecord.begin_date, {zone: config.locationTimeZone}).toISO() + : now.minus({days: 1}).start_of('day').toISO(); + endTime = now.toISO(); + } + } else { + startTime = DateTime.fromISO(selectedTimeSpan.start, {zone: config.locationTimeZone}).toISO(); + endTime = DateTime.fromISO(selectedTimeSpan.end, {zone: config.locationTimeZone}).endOf('day').toISO(); + } + } return { parameterCode, diff --git a/assets/src/scripts/monitoring-location/store/hydrograph-data.js b/assets/src/scripts/monitoring-location/store/hydrograph-data.js index df0efd8c2..fdb66db70 100644 --- a/assets/src/scripts/monitoring-location/store/hydrograph-data.js +++ b/assets/src/scripts/monitoring-location/store/hydrograph-data.js @@ -9,7 +9,7 @@ import {fetchTimeSeries} from 'ui/web-services/instantaneous-values'; import {fetchSiteStatistics} from 'ui/web-services/statistics-data'; import {fetchDataFromSensorThings} from 'ui/web-services/sensor-things'; -import {isCalculatedTemperature, getConvertedTemperatureParameter} from 'ml/iv-data-utils'; +import {isCalculatedTemperature, getConvertedTemperatureParameter} from 'ml/parameter-code-utils'; const getParameterToFetch = function(parameterCode) { diff --git a/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js b/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js index 5b63103a6..64d88eaf4 100644 --- a/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js +++ b/assets/src/scripts/monitoring-location/store/hydrograph-parameters.js @@ -6,7 +6,7 @@ import config from 'ui/config'; import {fetchGroundwaterLevels} from 'ui/web-services/groundwater-levels'; import {fetchTimeSeries} from 'ui/web-services/instantaneous-values'; -import {getConvertedTemperatureParameter, hasMeasuredFahrenheitParameter} from 'ml/iv-data-utils'; +import {getConvertedTemperatureParameter, hasMeasuredFahrenheitParameter} from 'ml/parameter-code-utils'; import {Actions as floodStateActions} from './flood-data'; /* diff --git a/assets/src/scripts/utils.js b/assets/src/scripts/utils.js index fa51a7cea..b12de2386 100644 --- a/assets/src/scripts/utils.js +++ b/assets/src/scripts/utils.js @@ -1,6 +1,5 @@ import {bisector} from 'd3-array'; import {select} from 'd3-selection'; -import {DateTime} from 'luxon'; /** * Determine the unicode variant of an HTML decimal entity @@ -143,34 +142,6 @@ export const mediaQuery = function(minWidth) { return window.matchMedia(`screen and (min-width: ${minWidth}px)`).matches; }; -/** - * Calculate the start time of a time range based on a time-delta string and the end time - * - * @param {String} period -- ISO duration for date range of the time series - which has the form of something like - * P7D in which the 'P' stands for 'period', the '7' is units in the period, and 'D' is the type of unit - * in this case 'Days.' There are only two unit types here, 'Days' or 'Years' - * @param {Number} endTime -- the end time as universal time - * @param {String} ianaTimeZone -- Internet Assigned Numbers Authority designation for a time zone - * @returns {Number} the start time as universal time - */ -export const calcStartTime = function(period, endTime, ianaTimeZone) { - const timePeriodCode = period !== null ? period.substr(period.length - 1) : null; - const timePeriod = period !== null ? period.slice(1,-1) : null; - const hoursInOneYear = 8760; - - let startTime = DateTime.fromMillis(endTime, {zone: ianaTimeZone}); - - if (timePeriodCode === 'D') { - startTime = startTime.minus({days: timePeriod}); - } else if (timePeriodCode === 'Y') { - startTime = startTime.minus({hours: hoursInOneYear * timePeriod}); - } else { - console.log('No known period specified'); - } - - return startTime.valueOf(); -}; - /** * Returns a function that will be a no-op if a condition is false. * Use to insert conditionals into declarative-style D3-chained method calls. diff --git a/assets/src/scripts/utils.test.js b/assets/src/scripts/utils.test.js index 0aa82becf..ed734a91b 100644 --- a/assets/src/scripts/utils.test.js +++ b/assets/src/scripts/utils.test.js @@ -1,6 +1,6 @@ import { unicodeHtmlEntity, getHtmlFromString, replaceHtmlEntities, setEquality, - calcStartTime, callIf, parseRDB, convertFahrenheitToCelsius, + callIf, parseRDB, convertFahrenheitToCelsius, convertCelsiusToFahrenheit, sortedParameters, getNearestTime} from './utils'; @@ -61,24 +61,6 @@ describe('Utils module', () => { // Tests for wrap and matchMedia both require a real DOM rather than jsDOM so those functions are not tested. // For places where those functions are used in the tests, they have been mocked. - describe('calcStartTime', () => { - - const someDate = 1490562900000; - const timeZone = 'America/Chicago'; - - it('correctly handles a seven day interval', () => { - expect(calcStartTime('P7D', someDate, timeZone)).toEqual(1489958100000); - }); - - it('correctly handles a 30 day interval', () => { - expect(calcStartTime('P30D', someDate, timeZone)).toEqual(1487974500000); - }); - - it('correctly handles a year interval', () => { - expect(calcStartTime('P1Y', someDate, timeZone)).toEqual(1459026900000); - }); - }); - describe('callIf', () => { let spy; diff --git a/assets/src/scripts/web-services/groundwater-levels.js b/assets/src/scripts/web-services/groundwater-levels.js index bfb89ea48..4430eec13 100644 --- a/assets/src/scripts/web-services/groundwater-levels.js +++ b/assets/src/scripts/web-services/groundwater-levels.js @@ -46,8 +46,11 @@ export const fetchGroundwaterLevels = async function({siteno, parameterCode=null endTime: endTime, format: 'json' }); + const request = new Request(url, { + cache: 'no-store' + }); try { - const response = await fetch(url, { + const response = await fetch(request, { method: 'GET' }); if (response.status === 200) { diff --git a/assets/src/styles/components/hydrograph/_app.scss b/assets/src/styles/components/hydrograph/_app.scss index 51007bbc5..98f4a09b0 100644 --- a/assets/src/styles/components/hydrograph/_app.scss +++ b/assets/src/styles/components/hydrograph/_app.scss @@ -7,15 +7,16 @@ @use './graph'; .wdfn-component[data-component="hydrograph"] { - .short-cut-days-before-container { - div { - display: inline-block; - - .usa-radio { - display: inline-block; - @include uswds.u-margin-right(1); + .short-cut-time-span-container { + .usa-form { + @include uswds.at-media('tablet') { + @include uswds.u-maxw('full'); } } + .usa-radio { + display: inline-block; + @include uswds.u-margin-right(1); + } } #change-time-span-container { diff --git a/wdfn-server/waterdata/templates/macros/components.html b/wdfn-server/waterdata/templates/macros/components.html index 784bfe7b3..8c1553ec4 100644 --- a/wdfn-server/waterdata/templates/macros/components.html +++ b/wdfn-server/waterdata/templates/macros/components.html @@ -4,7 +4,7 @@ data-parameter-code="{{ default_parameter_code }}"> <div id="time-download-graph-controls"> {% if iv_period_of_record or gw_period_of_record %} - <div class="short-cut-days-before-container"></div> + <div class="short-cut-time-span-container"></div> {% endif %} <div class="select-actions-container"></div> </div> -- GitLab