diff --git a/server/models/views/lib/find-min-threshold.js b/server/models/views/lib/find-min-threshold.js index cf0909c36..360990474 100644 --- a/server/models/views/lib/find-min-threshold.js +++ b/server/models/views/lib/find-min-threshold.js @@ -31,7 +31,7 @@ function filterImtdThresholds (imtdThresholds) { return { alert: minObjectA ? minObjectA.value : null, - warning: minObjectW ? minObjectW.value : null + warning: minObjectW || null } } diff --git a/server/models/views/lib/latest-levels.js b/server/models/views/lib/latest-levels.js index eb083d682..0161eaf71 100644 --- a/server/models/views/lib/latest-levels.js +++ b/server/models/views/lib/latest-levels.js @@ -1,5 +1,5 @@ const { formatElapsedTime } = require('../../../util') -const { processThreshold } = require('./process-imtd-thresholds') // Import processThreshold from the module +const processThreshold = require('./process-threshold') // Import processThreshold from the module const WARNING_THRESHOLD_TYPES = ['FW RES FW', 'FW ACT FW', 'FW ACTCON FW'] diff --git a/server/models/views/lib/process-imtd-thresholds.js b/server/models/views/lib/process-imtd-thresholds.js index 56e17a045..cb2cc284b 100644 --- a/server/models/views/lib/process-imtd-thresholds.js +++ b/server/models/views/lib/process-imtd-thresholds.js @@ -1,46 +1,82 @@ -function processThreshold (threshold, stationStageDatum, stationSubtract, postProcess) { - if (threshold) { - if (postProcess) { - if (stationStageDatum > 0) { - threshold = threshold - stationStageDatum - } else if (stationStageDatum <= 0 && stationSubtract > 0) { - threshold = threshold - stationSubtract - } else { - return parseFloat(threshold).toFixed(2) - } - } - return parseFloat(threshold).toFixed(2) - } - return null -} +const processThreshold = require('./process-threshold') -function processImtdThresholds (imtdThresholds, stationStageDatum, stationSubtract, postProcess) { +const TOP_OF_NORMAL_RANGE = 'Top of normal range' + +function processImtdThresholds (imtdThresholds, stationStageDatum, stationSubtract, postProcess, pc5) { const thresholds = [] - const imtdThresholdWarning = processThreshold(imtdThresholds?.warning, stationStageDatum, stationSubtract, postProcess) + const imtdThresholdWarning = calculateWarningThreshold(imtdThresholds, stationStageDatum, stationSubtract, postProcess) + const imtdThresholdAlert = calculateAlertThreshold(imtdThresholds, stationStageDatum, stationSubtract, postProcess, pc5) + if (imtdThresholdWarning) { - // Correct threshold value if value > zero (Above Ordnance Datum) [FSR-595] + thresholds.push(imtdThresholdWarning) + } + + if (imtdThresholdAlert.length > 0) { + for (const alert of imtdThresholdAlert) { + thresholds.push(alert) + } + } else if (pc5) { thresholds.push({ + id: 'pc5', + description: 'Top of normal range. Low lying land flooding possible above this level', + shortname: TOP_OF_NORMAL_RANGE, + value: pc5 + }) + } else { return thresholds } + + return thresholds +} + +function calculateWarningThreshold (imtdThresholds, stationStageDatum, stationSubtract, postProcess) { + const imtdThresholdWarning = processThreshold(imtdThresholds?.warning?.value, stationStageDatum, stationSubtract, postProcess) + + if (imtdThresholdWarning) { + return { id: 'warningThreshold', - description: 'Property flooding is possible above this level. One or more flood warnings may be issued', + description: 'Property flooding is possible above this level', shortname: 'Possible flood warnings', value: imtdThresholdWarning - }) + } } + return null +} + +function calculateAlertThreshold (imtdThresholds, stationStageDatum, stationSubtract, postProcess, pc5) { const imtdThresholdAlert = processThreshold(imtdThresholds?.alert, stationStageDatum, stationSubtract, postProcess) + const imtdThresholdAlerts = [] if (imtdThresholdAlert) { - thresholds.push({ - id: 'alertThreshold', - description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued', - shortname: 'Possible flood alerts', - value: imtdThresholdAlert - }) + // First condition: if imtdThresholdAlert is not equal to Top of Normal Range (pc5) + if (Number(imtdThresholdAlert) !== Number(pc5)) { + imtdThresholdAlerts.push({ + id: 'alertThreshold', + description: 'Low lying land flooding possible above this level. One or more flood alerts may be issued', + shortname: 'Possible flood alerts', + value: imtdThresholdAlert + }) + } + // Second condition: if imtdThresholdAlert is equal to Top of Normal Range (pc5) + if (Number(imtdThresholdAlert) === Number(pc5)) { + imtdThresholdAlerts.push({ + id: 'alertThreshold', + description: 'Top of normal range. Low lying land flooding possible above this level. One or more flood alerts may be issued', + shortname: TOP_OF_NORMAL_RANGE, + value: imtdThresholdAlert + }) + } else if (Number(pc5)) { + imtdThresholdAlerts.push({ + id: 'pc5', + description: TOP_OF_NORMAL_RANGE, + shortname: TOP_OF_NORMAL_RANGE, + value: parseFloat(Number(pc5)).toFixed(2) + }) + } else { + return imtdThresholdAlerts + } } - return thresholds -} -module.exports = { - processImtdThresholds, - processThreshold + return imtdThresholdAlerts } + +module.exports = processImtdThresholds diff --git a/server/models/views/lib/process-threshold.js b/server/models/views/lib/process-threshold.js new file mode 100644 index 000000000..a8519eb92 --- /dev/null +++ b/server/models/views/lib/process-threshold.js @@ -0,0 +1,17 @@ +const processThreshold = (threshold, stationStageDatum, stationSubtract, postProcess) => { + if (threshold) { + if (postProcess) { + if (stationStageDatum > 0) { + threshold = threshold - stationStageDatum + } else if (stationStageDatum <= 0 && stationSubtract > 0) { + threshold = threshold - stationSubtract + } else { + return parseFloat(threshold).toFixed(2) + } + } + return parseFloat(threshold).toFixed(2) + } + return null +} + +module.exports = processThreshold diff --git a/server/models/views/lib/process-warning-thresholds.js b/server/models/views/lib/process-warning-thresholds.js new file mode 100644 index 000000000..3cca26d70 --- /dev/null +++ b/server/models/views/lib/process-warning-thresholds.js @@ -0,0 +1,65 @@ +const processThreshold = require('./process-threshold') + +const FLOOD_WARNING_THRESHOLD = 2 +const SEVERE_FLOOD_WARNING_THRESHOLD = 3 +function filterThresholdsBySeverity (thresholds) { + return thresholds.filter(item => + item.fwis_type === 'W' && + item.severity_value !== null && + (item.severity_value === FLOOD_WARNING_THRESHOLD || item.severity_value === SEVERE_FLOOD_WARNING_THRESHOLD) + ) +} + +function getMaxForEachFwisCode (thresholds) { + const maxValuesByFwisCode = {} + + thresholds.forEach(threshold => { + const fwisCode = threshold.fwis_code + const severityValue = threshold.severity_value + const thresholdValue = threshold.value + + // Check if there's already a threshold for this fwis_code + if (!maxValuesByFwisCode[fwisCode]) { + // If it's the first threshold for this fwis_code, store it + maxValuesByFwisCode[fwisCode] = threshold + } else { + const currentMax = maxValuesByFwisCode[fwisCode] + + // Compare based on severity_value first, or if severity_value is the same, compare based on the threshold value + if (severityValue > currentMax.severity_value || + (severityValue === currentMax.severity_value && parseFloat(thresholdValue) > parseFloat(currentMax.value))) { + maxValuesByFwisCode[fwisCode] = threshold + } + } + }) + + return Object.values(maxValuesByFwisCode) +} + +function createWarningObject (threshold, stationStageDatum, stationSubtract, postProcess) { + const warningType = + threshold.severity_value === SEVERE_FLOOD_WARNING_THRESHOLD + ? 'Severe flood warning' + : 'Flood warning' + + const imtdThresholdWarning = processThreshold(parseFloat(threshold.threshold_value).toFixed(2), stationStageDatum, stationSubtract, postProcess) + + return { + id: 'warningThreshold', + description: `${warningType} issued: ${threshold.ta_name}`, + shortname: `${threshold.ta_name}`, + value: imtdThresholdWarning + } +} +function processWarningThresholds (imtdThresholds, stationStageDatum, stationSubtract, postProcess) { + const filteredThresholds = filterThresholdsBySeverity(imtdThresholds) + const maxThresholdsByFwisCode = getMaxForEachFwisCode(filteredThresholds) + + const warningObjects = maxThresholdsByFwisCode.map(threshold => + createWarningObject(threshold, stationStageDatum, stationSubtract, postProcess) + ) + + return warningObjects +} + +module.exports = processWarningThresholds diff --git a/server/models/views/station.js b/server/models/views/station.js index 8f0ebc613..261de5612 100644 --- a/server/models/views/station.js +++ b/server/models/views/station.js @@ -5,7 +5,9 @@ const Station = require('./station-data') const Forecast = require('./station-forecast') const util = require('../../util') const tz = 'Europe/London' -const { processImtdThresholds, processThreshold } = require('./lib/process-imtd-thresholds') +const processImtdThresholds = require('./lib/process-imtd-thresholds') +const processThreshold = require('./lib/process-threshold') +const processWarningThresholds = require('./lib/process-warning-thresholds') const filterImtdThresholds = require('./lib/find-min-threshold') const bannerIconId3 = 3 @@ -250,11 +252,21 @@ class ViewModel { id: 'highest', value: this.station.porMaxValue, description: this.station.thresholdPorMaxDate - ? 'Water reaches the highest level recorded at this measuring station (recorded on ' + this.station.thresholdPorMaxDate + ')' + ? `Water reaches the highest level recorded at this measuring station (${this.station.thresholdPorMaxDate})` : 'Water reaches the highest level recorded at this measuring station', shortname: 'Highest level on record' }) } + + if (imtdThresholds?.length > 0) { + const processedWarningThresholds = processWarningThresholds( + imtdThresholds, + this.station.stageDatum, + this.station.subtract, + this.station.post_process) + thresholds.push(...processedWarningThresholds) + } + this.imtdThresholds = imtdThresholds?.length > 0 ? filterImtdThresholds(imtdThresholds) : [] @@ -263,20 +275,11 @@ class ViewModel { this.imtdThresholds, this.station.stageDatum, this.station.subtract, - this.station.post_process + this.station.post_process, + this.station.percentile5 ) thresholds.push(...processedImtdThresholds) - if (this.station.percentile5) { - // Only push typical range if it has a percentil5 - thresholds.push({ - id: 'pc5', - value: this.station.percentile5, - description: 'This is the top of the normal range', - shortname: TOP_OF_NORMAL_RANGE - }) - } - // Handle chartThreshold: add tidThreshold if a valid tid is present; if not, fallback to 'pc5'; if 'pc5' is unavailable, use 'alertThreshold' with "Top of normal range" description. // Extract tid from request URL if valid let tid = null diff --git a/server/src/js/components/map/tooltip.js b/server/src/js/components/map/tooltip.js deleted file mode 100644 index 2a1e47db0..000000000 --- a/server/src/js/components/map/tooltip.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict' - -// Tooltip component - -const { xhr } = window.flood.utils - -// ie11 closest() polyfill -const Element = window.Element -if (!Element.prototype.matches) { - Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector -} -if (!Element.prototype.closest) { - Element.prototype.closest = (s) => { - let el = this - do { - if (Element.prototype.matches.call(el, s)) return el - el = el.parentElement || el.parentNode - } while (el !== null && el.nodeType === 1) - return null - } -} - -const Tooltips = (options) => { - // Setup defaults - const defaults = { - margin: 15 - } - options = Object.assign({}, defaults, options) - - // Add tooltip - const addTooltip = (tool) => { - // Outer margin - const viewportMargin = options.margin - // Open tooltip first so we can get dimensions - tool.parentNode.classList.add('defra-tooltip--open') - // Typically more text so width is fixed - if (tool.tagName === 'A') { - tool.parentNode.classList.add('defra-tooltip--fixed-width') - } - // Determin position of overlay - const tooltip = tool.parentNode - const tooltipLeft = tooltip.getBoundingClientRect().left - const tooltipWidth = tooltip.getBoundingClientRect().width - const tip = tool.nextElementSibling - const tipWidth = tip.getBoundingClientRect().width - // Centre tip - let tipOffsetX = ((tipWidth - tooltipWidth) / 2) - (((tipWidth - tooltipWidth) / 2) * 2) - // Correct offset if near sides of govuk-width-container - const newTipLeft = tooltipLeft + tipOffsetX - const viewportWidth = window.innerWidth - if (newTipLeft < viewportMargin) { - // Left side - tipOffsetX = ((tooltipLeft - viewportMargin) / 2) - (((tooltipLeft - viewportMargin) / 2) * 2) - } else if ((newTipLeft + tipWidth) > (viewportWidth - viewportMargin)) { - // Right side - tipOffsetX = tipOffsetX - (newTipLeft + tipWidth - (viewportWidth - viewportMargin)) - } - - // Switch position if near top - if (tip.getBoundingClientRect().top < viewportMargin) { - tool.parentNode.classList.add('defra-tooltip--bottom') - } - tip.style.marginLeft = `${tipOffsetX}px` - } - - // Remove tooltip - const removeTooltips = () => { - const tooltips = document.querySelectorAll('.defra-tooltip--open') - if (tooltips.length) { - tooltips.forEach((tooltip) => { - tooltip.classList.remove('defra-tooltip--open') - tooltip.classList.remove('defra-tooltip--bottom') - tooltip.classList.remove('defra-tooltip--keyboard-focus') - const tip = tooltip.querySelector('.defra-tooltip__tip') - tip.style.removeProperty('margin-left') - // xhr tooltips - const status = tooltip.querySelector('[role="status"]') - if (status) { status.innerHTML = '' } - }) - } - } - - // Add on click (xhr tooltip only) - document.addEventListener('click', (e) => { - const tool = e.target.closest('.defra-tooltip__tool') - const isXhrTool = tool && tool.tagName === 'A' - // Remove tooltips when clicking outside unless its a basic tooltip - if (!tool || isXhrTool) { removeTooltips() } - if (tool) { e.preventDefault() } - // Only intersted in xhrTools from here on - if (!isXhrTool) { return } - // Get content - const content = tool.nextElementSibling - const url = tool.href.split(/\?|#/)[0] - // XMLHttpRequest - xhr(url, (err, response) => { - if (err) { - console.log('Error: ' + err) - } else { - content.innerHTML = '' - const fragmentId = tool.href.substring(tool.href.indexOf('#')) - const fragment = response.querySelector(`${fragmentId}`) - // Remove any hyperlinks - fragment.querySelectorAll('a').forEach(link => { - link.outerHTML = link.innerHTML - }) - content.appendChild(fragment) - addTooltip(tool) - } - }, 'document') - }) - - // Remove on escape - document.addEventListener('keyup', (e) => { - if (e.key === 'Escape' || e.key === 'Esc') { - removeTooltips() - } - }) - - // Add on mouse enter (basic tooltip only) - document.addEventListener('mouseenter', (e) => { - const isTooltip = e.target.classList && e.target.classList.contains('defra-tooltip') - if (isTooltip && e.target.firstElementChild.tagName !== 'A') { - removeTooltips() - addTooltip(e.target.firstElementChild) - } - }, true) - - // Remove on mouse leave (basic tooltip only) - document.addEventListener('mouseleave', (e) => { - const isTooltip = e.target.classList && e.target.classList.contains('defra-tooltip') - if (isTooltip && e.target.firstElementChild.tagName !== 'A') { - removeTooltips() - } - }, true) - - // Add on focus (basic tooltip only) - document.addEventListener('focusin', (e) => { - const isTool = e.target.classList.contains('defra-tooltip__tool') - if (isTool && e.target.tagName !== 'A') { - removeTooltips() - addTooltip(e.target) - } - }) - - // Remove on blur (basic and xhr tooltips) - document.addEventListener('focusout', (e) => { - const isTool = e.target.classList.contains('defra-tooltip__tool') - if (isTool) { - e.target.parentNode.classList.remove('defra-tooltip--keyboard-focus') - removeTooltips() - } - }) -} - -window.flood.createTooltips = (options) => { - return Tooltips(options) -} diff --git a/server/src/js/components/tooltip.js b/server/src/js/components/tooltip.js new file mode 100644 index 000000000..460b8c49f --- /dev/null +++ b/server/src/js/components/tooltip.js @@ -0,0 +1,66 @@ +'use strict' + +// Tooltip component + +// ie11 closest() polyfill +const Element = window.Element +if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector +} +if (!Element.prototype.closest) { + Element.prototype.closest = (s) => { + let el = this + do { + if (Element.prototype.matches.call(el, s)) return el + el = el.parentElement || el.parentNode + } while (el !== null && el.nodeType === 1) + return null + } +} + +// Tooltip component +const Tooltips = () => { + const tooltips = document.querySelectorAll('[data-tooltip]') + + tooltips.forEach(tooltip => { + let timeout + + // Add on mouse enter (basic tooltip only) + tooltip.addEventListener('mouseenter', () => { + timeout = setTimeout(() => { + tooltip.classList.add('defra-tooltip--visible') + }, 500) + }) + + // Remove on mouse leave (basic tooltip only) + tooltip.addEventListener('mouseleave', () => { + clearTimeout(timeout) + tooltip.classList.remove('defra-tooltip--visible') + }) + + // Remove on escape + document.addEventListener('keyup', e => { + if (e.key === 'Escape' || e.key === 'Esc') { + clearTimeout(timeout) + tooltip.classList.remove('defra-tooltip--visible') + } + }) + + // Add on focus (basic tooltip only) + tooltip.addEventListener('focusin', (e) => { + timeout = setTimeout(() => { + tooltip.classList.add('defra-tooltip--visible') + }, 500) + }) + + // Remove on blur (basic and xhr tooltips) + tooltip.addEventListener('focusout', (e) => { + clearTimeout(timeout) + tooltip.classList.remove('defra-tooltip--visible') + }) + }) +} + +window.flood.createTooltips = (options) => { + return Tooltips(options) +} diff --git a/server/src/js/core.js b/server/src/js/core.js index fd8326c7c..66e578ae8 100644 --- a/server/src/js/core.js +++ b/server/src/js/core.js @@ -13,6 +13,7 @@ import './components/map/outlook' import './components/levels-table' import './components/toggle-list-display' import './components/toggletip' +import './components/tooltip' document.addEventListener('readystatechange', () => { if (document.readyState === 'interactive') { @@ -72,6 +73,9 @@ document.addEventListener('readystatechange', () => { const elem = document.getElementById('cookie-banner') let calledGTag = false + // Add tooltips + window.flood.createTooltips() + // Check not on cookie settings page if (elem) { const seenCookieMessage = /(^|;)\s*seen_cookie_message=/.test(document.cookie) diff --git a/server/src/sass/application.scss b/server/src/sass/application.scss index e35ed622f..bcc2c2a08 100644 --- a/server/src/sass/application.scss +++ b/server/src/sass/application.scss @@ -68,6 +68,7 @@ $govuk-breakpoints: ( @import "components/stations-overview"; @import "components/chart-controls"; @import "components/toggletip"; +@import "components/tooltip"; @import "components/navbar"; @import "components/flood-levels-table"; diff --git a/server/src/sass/components/_flood-impact-list.scss b/server/src/sass/components/_flood-impact-list.scss index 823b75117..356b75b19 100644 --- a/server/src/sass/components/_flood-impact-list.scss +++ b/server/src/sass/components/_flood-impact-list.scss @@ -1,54 +1,56 @@ .defra-flood-impact-list { @include govuk-font($size: 19); - & { - margin-top: 0px; - border-bottom: 1px solid $govuk-border-colour; - } + margin-top: 0px; + border-bottom: 1px solid $govuk-border-colour; + &__row { position: relative; margin-left: 3.65em; overflow: visible; } + &__row:before { position: absolute; content: ''; width: 3px; top: 11px; - bottom: -20px; + bottom: -22px; color: govuk-colour('mid-grey'); border-left: 3px solid currentcolor; margin-left: -1px; left: 8%; + @include mq ($from: tablet) { - left: 4%; + left: 4%; } } + &__key { @include govuk-font($size: false, $tabular: true, $weight: bold); - & { - position: absolute; - left: -3.65em; - width: 3.65em; - top: 0; - text-align: right; - } + position: absolute; + left: -3.65em; + width: 3.65em; + top: 0; + text-align: right; } + &__value { position: relative; margin: 0; padding-left: 16%; - padding-bottom: 10px; + @include mq ($from: tablet) { - padding-left: 8%; - padding-bottom: 15px; + padding-left: 8%; } } + &__value--latest { - font-weight: bold; + font-weight: bold; } + &__value:nth-child(2):before { position: absolute; - content:''; + content: ''; width: 13px; height: 3px; color: govuk-colour('mid-grey'); @@ -58,99 +60,122 @@ margin-left: -6px; top: 8px; left: 8%; + @include mq ($from: tablet) { top: 11px; left: 4%; } } + &__row:last-child { &:before { bottom: 0; } + .defra-flood-impact-list__value:last-child { - padding-bottom:0; + padding-bottom: 0; } + .defra-flood-impact-list__value:last-child { padding-bottom: 0; + .defra-flood-impact-list__container { border-bottom: 0; } } } + &__container { - padding-bottom:10px; - border-bottom:1px solid $govuk-border-colour; + padding-bottom: 10px; + padding-right: 15%; + @include mq ($from: tablet) { padding-right: 28%; padding-bottom: 15px; } } - &__container button { - margin-top: 5px; - @include mq ($from: tablet) { - position:absolute; - right: 0; - top: 0; - margin-top: 0; - } + + &__container .defra-tooltip { + margin-top: 10px; + position: absolute; + right: 0; + top: 0; + margin-top: 0; + } + + + dd:has(+ [style*="display: none"]) &__container, + dd:last-of-type &__container { + border-bottom: 1px solid $govuk-border-colour; + margin-bottom: 10px; } + .defra-button-text-s { - display:block; + display: block; } + &__row--current .defra-flood-impact-list__action { display: none; } + &__row--exceeded { - &:before { - margin-left: -2px; - color: govuk-colour('blue'); - border-left: 5px solid currentcolor; - } - .defra-flood-impact-list__value:nth-child(2):before { - margin-left: -6px; - color: govuk-colour('blue'); - border-top: 5px solid currentcolor; - } + &:before { + margin-left: -2px; + color: govuk-colour('blue'); + border-left: 5px solid currentcolor; + } + + .defra-flood-impact-list__value:nth-child(2):before { + margin-left: -6px; + color: govuk-colour('blue'); + border-top: 5px solid currentcolor; + } } + &__row--current { - &:before { - margin-left: -2px; - color: govuk-colour('blue'); - border-left: 5px solid currentcolor; - } - .defra-flood-impact-list__value:before { - margin-left: -6px; - color: govuk-colour('blue'); - border-top: 5px solid currentcolor; - } + &:before { + margin-left: -2px; + color: govuk-colour('blue'); + border-left: 5px solid currentcolor; + } + + .defra-flood-impact-list__value:before { + margin-left: -6px; + color: govuk-colour('blue'); + border-top: 5px solid currentcolor; + } } + &__alert { - z-index:1; + z-index: 1; text-indent: -5000px; - position:absolute; + position: absolute; background-image: svg-url(''); background-repeat: no-repeat; background-position: top left; - background-color:white; - border-top:1px solid white; - border-bottom:1px solid white; - width:32px; - height:32px; + background-color: white; + border-top: 1px solid white; + border-bottom: 1px solid white; + width: 32px; + height: 32px; background-size: 32px 32px; - margin-left:-16px; - top:-6px; - left:8%; + margin-left: -16px; + top: -6px; + left: 8%; + @include mq ($from: tablet) { - top:-4px; - left:4%; + top: -4px; + left: 4%; } } + &__warning { @extend .defra-flood-impact-list__alert; background-image: svg-url(''); } + &__severe { @extend .defra-flood-impact-list__alert; background-image: svg-url(''); } - } +} diff --git a/server/src/sass/components/_latest-levels-box.scss b/server/src/sass/components/_latest-levels-box.scss index 599291e5e..25f45143b 100644 --- a/server/src/sass/components/_latest-levels-box.scss +++ b/server/src/sass/components/_latest-levels-box.scss @@ -9,6 +9,8 @@ @include govuk-font($size: 16); margin-top: 0px; margin-bottom: 15px; + padding-top: 5px; + padding-bottom: 4px; font-weight: bold; text-transform: uppercase; background-color: #1C70B8; @@ -27,4 +29,4 @@ color: $govuk-secondary-text-colour; margin: 0; } -} \ No newline at end of file +} diff --git a/server/src/sass/components/_toggle-list-display.scss b/server/src/sass/components/_toggle-list-display.scss index f920510e2..3217989a3 100644 --- a/server/src/sass/components/_toggle-list-display.scss +++ b/server/src/sass/components/_toggle-list-display.scss @@ -1,3 +1,4 @@ -.js-enabled [data-toggle-list-display-item] { +.js-enabled [data-toggle-list-display]:not([data-toggle-list-display="warning-default"]) { display: none; -} + } + \ No newline at end of file diff --git a/server/src/sass/components/_toggletip.scss b/server/src/sass/components/_toggletip.scss index 94521f9ef..abaa3e890 100644 --- a/server/src/sass/components/_toggletip.scss +++ b/server/src/sass/components/_toggletip.scss @@ -1,12 +1,10 @@ .defra-toggletip { - height: 1rem; + // height: 1rem; &:first-child { display: inline-block; } - & { - display: inline-block; - position: relative; - } + display: inline-block; + position: relative; &:not(.defra-toggletip-target) { padding-right: 26px; } @@ -19,11 +17,9 @@ @include mq ($from: tablet) { top: -1px; } - & { - right: 0px; - width: 26px; - height: 26px; - } + right: 0px; + width: 26px; + height: 26px; } &__button { position: absolute; @@ -69,21 +65,17 @@ } &__info { @include govuk-font($size: 16); - & { - cursor: auto; - } - & { - position:absolute; - width:0px; - overflow: hidden; - box-sizing: border-box; - visibility: hidden; - top: auto; - bottom: 100%; - left: 0px; - padding-bottom: 10px; - text-align: left; - } + cursor: auto; + position:absolute; + width:0px; + overflow: hidden; + box-sizing: border-box; + visibility: hidden; + top: auto; + bottom: 100%; + left: 0px; + padding-bottom: 10px; + text-align: left; } &__text { position: relative; diff --git a/server/src/sass/components/_tooltip.scss b/server/src/sass/components/_tooltip.scss new file mode 100644 index 000000000..9f4909e7f --- /dev/null +++ b/server/src/sass/components/_tooltip.scss @@ -0,0 +1,121 @@ +.defra-tooltip { + position: relative; +} +.defra-tooltip__label { + display: none; + position: absolute; +} +.defra-tooltip__label-inner { + position: relative; + display: inline-block; + vertical-align: middle; + width: max-content; + max-width: 200px; + padding: 4px 5px; + background: govuk-colour(black); + border: 2px solid govuk-colour(black); + margin-bottom: 0; + &:after { + content: ''; + position: absolute; + box-sizing: border-box; + z-index: 1; + width: 10px; + height: 10px; + color: govuk-colour(black); + border: 5px solid currentcolor; + -webkit-transform: rotate(135deg); + -moz-transform: rotate(135deg); + -o-transform: rotate(135deg); + transform: rotate(135deg); + } +} +.defra-tooltip__label-inner.govuk-body-s { + color: white; + margin-bottom: 0; +} + +// Above and below +.defra-tooltip--below .defra-tooltip__label, +.defra-tooltip--above .defra-tooltip__label { + width: 200px; + left: 50%; + text-align: center; + margin-left: -100px; +} + +.defra-tooltip--below .defra-tooltip__label { + top: 100%; +} + +.defra-tooltip--above .defra-tooltip__label { + bottom: 100%; +} + +.defra-tooltip--below .defra-tooltip__label-inner, +.defra-tooltip--above .defra-tooltip__label-inner { + &:after { + left: 50%; + margin-left: -4px; + } +} +.defra-tooltip--below .defra-tooltip__label-inner { + margin-top: 10px; + &:after { + top: -6px; + } +} + +.defra-tooltip--above .defra-tooltip__label-inner { + margin-bottom: 10px; + &:after { + bottom: -6px; + } +} + +// Left and right +.defra-tooltip--left .defra-tooltip__label, +.defra-tooltip--right .defra-tooltip__label { + height: 150%; + top: -25%; + // &:before { + // content: ''; + // display: inline-block; + // height: 100%; + // vertical-align: middle; + // } +} + +.defra-tooltip--left .defra-tooltip__label-inner, +.defra-tooltip--right .defra-tooltip__label-inner { + &:after { + top: 50%; + margin-top: -4px; + } +} + +.defra-tooltip--left .defra-tooltip__label { + right: 100%; +} + +.defra-tooltip--left .defra-tooltip__label-inner { + margin-right: 10px; + &:after { + right: -6px; + } +} + +.defra-tooltip--right .defra-tooltip__label { + left: 100%; +} + +.defra-tooltip--right .defra-tooltip__label-inner { + margin-left: 10px; + &:after { + left: -6px; + } +} + +.defra-tooltip--visible .defra-tooltip__label { + display: block; +} diff --git a/server/views/partials/latest-levels.html b/server/views/partials/latest-levels.html index 2909c2171..c0776eb5f 100644 --- a/server/views/partials/latest-levels.html +++ b/server/views/partials/latest-levels.html @@ -21,4 +21,4 @@

Latest level{% if model.latestLevels.length > 1 %} {% endfor %}

{% if model.latestLevels.length > 1 %}These levels{% else %}This level{% endif %} will update automatically

- \ No newline at end of file + diff --git a/server/views/station.html b/server/views/station.html index 0985a0c08..0645c2c93 100644 --- a/server/views/station.html +++ b/server/views/station.html @@ -271,10 +271,12 @@

Height {% if model.thresholds.length > 0 %}
-

How levels here could affect nearby areas

- {% if model.station.hasImpacts %} +

How levels here could affect nearby areas

+
+ {% if model.station.hasImpacts %} {% endif %} +
{% for band in model.thresholds %}
diff --git a/test/models/floods.js b/test/data/floods.js similarity index 100% rename from test/models/floods.js rename to test/data/floods.js diff --git a/test/data/taThresholdsData.json b/test/data/taThresholdsData.json index 834813d63..8fa9583cb 100644 --- a/test/data/taThresholdsData.json +++ b/test/data/taThresholdsData.json @@ -2,6 +2,7 @@ "singleActiveOffline": [ { "rloi_id": 7173, + "station_threshold_id": 123465, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Active", @@ -16,6 +17,7 @@ "singleSuspended": [ { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Pinn", "agency_name": "Moss Close", "status": "Suspended", @@ -30,6 +32,7 @@ "singleClosed": [ { "rloi_id": 7202, + "station_threshold_id": 123458, "river_name": "River Test", "agency_name": "Test Road", "status": "Closed", @@ -44,6 +47,7 @@ "multipleNormalClosed": [ { "rloi_id": 7174, + "station_threshold_id": 123459, "river_name": "River Pinn", "agency_name": "Eastcote Road", "status": "Active", @@ -56,6 +60,7 @@ }, { "rloi_id": 7202, + "station_threshold_id": 123458, "river_name": "River Test", "agency_name": "Test Road", "status": "Closed", @@ -70,6 +75,7 @@ "multipleNormalActiveOfflineWelshNoValues": [ { "rloi_id": 7174, + "station_threshold_id": 123459, "river_name": "River Pinn", "agency_name": "Eastcote Road", "status": "Active", @@ -82,6 +88,7 @@ }, { "rloi_id": 7173, + "station_threshold_id": 123465, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Active", @@ -94,6 +101,7 @@ }, { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Welsh", "agency_name": "Welsh Station", "status": "Active", @@ -108,6 +116,7 @@ "multipleNormalSuspendedClosed": [ { "rloi_id": 7174, + "station_threshold_id": 123459, "river_name": "River Pinn", "agency_name": "Eastcote Road", "status": "Active", @@ -120,6 +129,7 @@ }, { "rloi_id": 7173, + "station_threshold_id": 123465, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Suspended", @@ -132,6 +142,7 @@ }, { "rloi_id": 7202, + "station_threshold_id": 123458, "river_name": "River Test", "agency_name": "Test Road", "status": "Closed", @@ -146,6 +157,7 @@ "allClosedOrWelshNoValues": [ { "rloi_id": 7202, + "station_threshold_id": 123458, "river_name": "River Test", "agency_name": "Test Road", "status": "Closed", @@ -158,6 +170,7 @@ }, { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Welsh", "agency_name": "Welsh Station", "status": "Active", @@ -172,6 +185,7 @@ "singleWelshWithValues": [ { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Welsh", "agency_name": "Welsh Station", "status": "Active", @@ -186,6 +200,7 @@ "multipleLatestLevels": [ { "rloi_id": 7174, + "station_threshold_id": 123459, "river_name": "River Pinn", "station_threshold_id": "1747544", "agency_name": "Eastcote Road", @@ -199,6 +214,7 @@ }, { "rloi_id": 7174, + "station_threshold_id": 123459, "river_name": "River Pinn", "agency_name": "Eastcote Road", "station_threshold_id": "1747544", @@ -212,6 +228,7 @@ }, { "rloi_id": 7173, + "station_threshold_id": 123465, "river_name": "River Pinn", "station_threshold_id": "1747543", "agency_name": "Avenue Road", @@ -225,6 +242,7 @@ }, { "rloi_id": 7173, + "station_threshold_id": 123465, "river_name": "River Pinn", "station_threshold_id": "1747543", "agency_name": "Avenue Road", @@ -238,6 +256,7 @@ }, { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Pinn", "station_threshold_id": "1747541", "agency_name": "Moss Close", @@ -251,6 +270,7 @@ }, { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Pinn", "station_threshold_id": "1747541", "agency_name": "Moss Close", @@ -266,6 +286,7 @@ "overLimitLatestLevels": [ { "rloi_id": 7174, + "station_threshold_id": 123459, "river_name": "River Pinn", "agency_name": "Eastcote Road", "status": "Active", @@ -278,6 +299,7 @@ }, { "rloi_id": 7174, + "station_threshold_id": 123459, "river_name": "River Pinn", "agency_name": "Eastcote Road", "status": "Active", @@ -290,6 +312,7 @@ }, { "rloi_id": 7173, + "station_threshold_id": 123465, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Active", @@ -302,6 +325,7 @@ }, { "rloi_id": 7173, + "station_threshold_id": 123465, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Active", @@ -314,6 +338,7 @@ }, { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Pinn", "agency_name": "Moss Close", "status": "Active", @@ -326,6 +351,7 @@ }, { "rloi_id": 7201, + "station_threshold_id": 123457, "river_name": "River Pinn", "agency_name": "Moss Close", "status": "Active", @@ -338,6 +364,7 @@ }, { "rloi_id": 1111, + "station_threshold_id": 123500, "river_name": "River Pinn", "agency_name": "Moss Close", "status": "Active", @@ -350,6 +377,7 @@ }, { "rloi_id": 1111, + "station_threshold_id": 123500, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Active", @@ -362,6 +390,7 @@ }, { "rloi_id": 1112, + "station_threshold_id": 123501, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Active", @@ -374,6 +403,7 @@ }, { "rloi_id": 1113, + "station_threshold_id": 123502, "river_name": "River Pinn", "agency_name": "Avenue Road", "status": "Active", diff --git a/test/data/warning.json b/test/data/warning.json new file mode 100644 index 000000000..cd72105e4 --- /dev/null +++ b/test/data/warning.json @@ -0,0 +1,19 @@ +{ + "station_threshold_id":"1749487", + "station_id":"7332", + "fwis_code":"062FWF46Harpendn", + "fwis_type":"W", + "direction":"u", + "value":"2.1", + "threshold_type":"FW RES FW", + "rloi_id":7332, + "river_name":"River Lee", + "agency_name":"Harpenden", + "status":"Active", + "iswales":false, + "latest_level":"0.935", + "threshold_value":"1.6", + "value_timestamp":"2024-09-26T15:30:00.000Z", + "ta_name":"River Lee at Harpenden", + "severity_value":2 +} diff --git a/test/data/warnings.json b/test/data/warnings.json new file mode 100644 index 000000000..4906bf292 --- /dev/null +++ b/test/data/warnings.json @@ -0,0 +1,98 @@ + +[ + { + "station_threshold_id": "1749487", + "station_id": "7332", + "fwis_code": "062FWF46Harpendn", + "fwis_type": "W", + "direction": "u", + "value": "2.1", + "threshold_type": "FW RES FW", + "rloi_id": 7332, + "river_name": "River Lee", + "agency_name": "Harpenden", + "status": "Active", + "iswales": false, + "latest_level": "0.935", + "threshold_value": "1.6", + "value_timestamp": "2024-09-26T15:30:00.000Z", + "ta_name": "River Lee at Harpenden", + "severity_value": 2 + }, + { + "station_threshold_id": "1749488", + "station_id": "7333", + "fwis_code": "063FWF46Oxford", + "fwis_type": "W", + "direction": "u", + "value": "2.3", + "threshold_type": "FW RES FW", + "rloi_id": 7333, + "river_name": "River Thames", + "agency_name": "Oxford", + "status": "Active", + "iswales": false, + "latest_level": "1.150", + "threshold_value": "1.8", + "value_timestamp": "2024-09-26T15:45:00.000Z", + "ta_name": "River Thames at Oxford", + "severity_value": 2 + }, + { + "station_threshold_id": "1749489", + "station_id": "7334", + "fwis_code": "064FWF46York", + "fwis_type": "W", + "direction": "u", + "value": "2.0", + "threshold_type": "FW RES FW", + "rloi_id": 7334, + "river_name": "River Ouse", + "agency_name": "York", + "status": "Active", + "iswales": false, + "latest_level": "1.050", + "threshold_value": "1.7", + "value_timestamp": "2024-09-26T16:00:00.000Z", + "ta_name": "River Ouse at York", + "severity_value": 3 + }, + { + "station_threshold_id": "1749490", + "station_id": "7335", + "fwis_code": "065FWF46Bath", + "fwis_type": "W", + "direction": "u", + "value": "2.2", + "threshold_type": "FW RES FW", + "rloi_id": 7335, + "river_name": "River Avon", + "agency_name": "Bath", + "status": "Active", + "iswales": false, + "latest_level": "1.100", + "threshold_value": "1.7", + "value_timestamp": "2024-09-26T16:15:00.000Z", + "ta_name": "River Avon at Bath", + "severity_value": 3 + }, + { + "station_threshold_id": "1749490", + "station_id": "7335", + "fwis_code": "065FWF46Bath", + "fwis_type": "W", + "direction": "u", + "value": "2.2", + "threshold_type": "FW RES FW", + "rloi_id": 7335, + "river_name": "River Avon", + "agency_name": "Bath", + "status": "Active", + "iswales": false, + "latest_level": "1.100", + "threshold_value": "1.4", + "value_timestamp": "2024-09-26T16:15:00.000Z", + "ta_name": "River Avon at Bath", + "severity_value": 3 + } +] diff --git a/test/models/lib/process-imtd-thresholds.js b/test/models/lib/process-imtd-thresholds.js index f6b31c698..797bb9605 100644 --- a/test/models/lib/process-imtd-thresholds.js +++ b/test/models/lib/process-imtd-thresholds.js @@ -1,10 +1,11 @@ const Lab = require('@hapi/lab') const Code = require('@hapi/code') +const data = require('../../data') const lab = exports.lab = Lab.script() -const { processImtdThresholds } = require('../../../server/models/views/lib/process-imtd-thresholds') +const processImtdThresholds = require('../../../server/models/views/lib/process-imtd-thresholds') -const alertExpectedText = { id: 'alertThreshold', description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued', shortname: 'Possible flood alerts' } -const warningExpectedText = { id: 'warningThreshold', description: 'Property flooding is possible above this level. One or more flood warnings may be issued', shortname: 'Possible flood warnings' } +const alertExpectedText = { id: 'alertThreshold', description: 'Top of normal range. Low lying land flooding possible above this level. One or more flood alerts may be issued', shortname: 'Top of normal range' } +const warningExpectedText = { id: 'warningThreshold', description: 'Property flooding is possible above this level', shortname: 'Possible flood warnings' } function expectThresholds (thresholds, warningThreshold, alertThreshold) { Code.expect(thresholds.length).to.equal(2) @@ -15,11 +16,11 @@ function expectThresholds (thresholds, warningThreshold, alertThreshold) { lab.experiment('process IMTD thresholds test', () => { lab.experiment('given post processing is set to false', () => { lab.test('then thresholds should be returned as is', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0, false) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0, false, 1.1) expectThresholds(thresholds, '2.10', '1.10') }) lab.test('then single null thresholds should not be returned', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: null }, 0, 0, false) + const thresholds = processImtdThresholds({ alert: 1.1, warning: null }, 0, 0, false, 1.1) Code.expect(thresholds.length).to.equal(1) Code.expect(thresholds[0]).to.equal({ ...alertExpectedText, value: '1.10' }) }) @@ -34,35 +35,35 @@ lab.experiment('process IMTD thresholds test', () => { }) lab.experiment('given post processing is set to true', () => { lab.test('then thresholds should be returned as is when stageDatum is zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0, true, 1.1) expectThresholds(thresholds, '2.10', '1.10') }) lab.test('then thresholds should be returned as adjusted when stageDatum is greater than zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 2.2, 0, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 2.2, 0, true, -1.1) expectThresholds(thresholds, '-0.10', '-1.10') }) lab.test('then thresholds should be returned as is when stageDatum is less than zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, -2.2, 0, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, -2.2, 0, true, 1.1) expectThresholds(thresholds, '2.10', '1.10') }) lab.test('then thresholds should be returned as adjusted when stageDatum is less than zero and stationSubtract is greater than zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, -2.2, 0.5, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, -2.2, 0.5, true, 0.6) expectThresholds(thresholds, '1.60', '0.60') }) lab.test('then thresholds should be returned as is when stageDatum is less than zero and stationSubtract is less than zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, -2.2, -0.5, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, -2.2, -0.5, true, 1.1) expectThresholds(thresholds, '2.10', '1.10') }) lab.test('then thresholds should be returned as is when stageDatum is equal to zero and stationSubtract is less than zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, -0.5, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, -0.5, true, 1.1) expectThresholds(thresholds, '2.10', '1.10') }) lab.test('then thresholds should be returned as adjusted when stageDatum is equal to zero and stationSubtract is greater than than zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0.5, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0.5, true, 0.6) expectThresholds(thresholds, '1.60', '0.60') }) lab.test('then thresholds should be returned as is when stageDatum is equal to zero and stationSubtract is zero', async () => { - const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0, true) + const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0, true, 1.1) expectThresholds(thresholds, '2.10', '1.10') }) }) diff --git a/test/models/lib/process-warning-thresholds.js b/test/models/lib/process-warning-thresholds.js new file mode 100644 index 000000000..4cc7d9e25 --- /dev/null +++ b/test/models/lib/process-warning-thresholds.js @@ -0,0 +1,41 @@ +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') +const data = require('../../data') +const lab = exports.lab = Lab.script() +const processWarningThresholds = require('../../../server/models/views/lib/process-warning-thresholds') + +// const warningExpectedText = { id: 'warningThreshold', description: 'Flood warning issued: River Lee at Harpenden', shortname: 'River Lee at Harpenden' } + +lab.experiment('process Warning thresholds test', () => { + lab.test('check correct number of warning thresholds are created', async () => { + const thresholds = processWarningThresholds(data.warnings) + console.log(thresholds) + Code.expect(thresholds.length).to.equal(4) + }) + lab.test('check warning threshold text', async () => { + const thresholds = processWarningThresholds(data.warnings) + Code.expect(thresholds[0].description).to.equal('Flood warning issued: River Lee at Harpenden') + Code.expect(thresholds[0].shortname).to.equal('River Lee at Harpenden') + }) + lab.test('check warning threshold value displays with 2 decimal places', async () => { + const thresholds = processWarningThresholds(data.warnings) + Code.expect(thresholds[0].value).to.equal('1.60') + }) + lab.test('check severe warning threshold text', async () => { + const thresholds = processWarningThresholds(data.warnings) + Code.expect(thresholds[2].description).to.equal('Severe flood warning issued: River Ouse at York') + Code.expect(thresholds[2].shortname).to.equal('River Ouse at York') + }) + lab.test('check severe warning threshold value with 2 decimal places', async () => { + const thresholds = processWarningThresholds(data.warnings) + Code.expect(thresholds[2].value).to.equal('1.70') + }) + lab.test('check only the highest warning is used when multiple', async () => { + const thresholds = processWarningThresholds(data.warnings) + Code.expect(thresholds[3].value).to.equal('1.70') + }) + lab.test('check no warning thresholds is handled', async () => { + const thresholds = processWarningThresholds([]) + Code.expect(thresholds).to.equal([]) + }) +}) diff --git a/test/models/station.js b/test/models/station.js index 82dffb513..793a23ce1 100644 --- a/test/models/station.js +++ b/test/models/station.js @@ -37,7 +37,7 @@ lab.experiment('Station model test', () => { Code.expect(Result.thresholds[0].values).to.equal([ { id: 'warningThreshold', - description: 'Property flooding is possible above this level. One or more flood warnings may be issued', + description: 'Property flooding is possible above this level', shortname: 'Possible flood warnings', value: '3.64' } @@ -45,7 +45,7 @@ lab.experiment('Station model test', () => { Code.expect(Result.thresholds[2].values).to.equal([ { id: 'alertThreshold', - description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued', + description: 'Low lying land flooding possible above this level. One or more flood alerts may be issued', shortname: 'Possible flood alerts', value: '3.22' } @@ -67,7 +67,7 @@ lab.experiment('Station model test', () => { Code.expect(Result.thresholds[1].values).to.equal([ { id: 'warningThreshold', - description: 'Property flooding is possible above this level. One or more flood warnings may be issued', + description: 'Property flooding is possible above this level', shortname: 'Possible flood warnings', value: '3.22' } @@ -75,13 +75,7 @@ lab.experiment('Station model test', () => { Code.expect(Result.thresholds[2].values).to.equal([ { id: 'alertThreshold', - description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued', - shortname: 'Possible flood alerts', - value: '2.84' - }, - { - description: 'This is the top of the normal range', - id: 'pc5', + description: 'Top of normal range. Low lying land flooding possible above this level. One or more flood alerts may be issued', shortname: 'Top of normal range', value: '2.84' } @@ -224,7 +218,7 @@ lab.experiment('Station model test', () => { Code.expect(Result.thresholds[0].values).to.equal( [ { - description: 'Property flooding is possible above this level. One or more flood warnings may be issued', + description: 'Property flooding is possible above this level', id: 'warningThreshold', shortname: 'Possible flood warnings', value: '4.20' @@ -235,7 +229,7 @@ lab.experiment('Station model test', () => { [ { id: 'alertThreshold', - description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued', + description: 'Low lying land flooding possible above this level. One or more flood alerts may be issued', shortname: 'Possible flood alerts', value: '3.88' }