diff --git a/assets/src/scripts/components/hydrograph/axes.js b/assets/src/scripts/components/hydrograph/axes.js index a2949c18720fe990575fdb9994dbd22d257106a0..e5dc6d8a572f65a964fbee7a606bd75b6eb10dc5 100644 --- a/assets/src/scripts/components/hydrograph/axes.js +++ b/assets/src/scripts/components/hydrograph/axes.js @@ -4,7 +4,7 @@ const { timeDay } = require('d3-time'); const { timeFormat } = require('d3-time-format'); const { createSelector } = require('reselect'); -const { WIDTH, HEIGHT, MARGIN } = require('./layout'); +const { layoutSelector, MARGIN } = require('./layout'); const { xScaleSelector, yScaleSelector } = require('./scales'); const yTickCount = 5; @@ -55,24 +55,26 @@ function createAxes({xScale, yScale}, yTickSize) { const axesSelector = createSelector( xScaleSelector('current'), yScaleSelector, - (state) => state.title, - (xScale, yScale, title) => { + layoutSelector, + (state) => state.plotYLabel, + (xScale, yScale, layout, plotYLabel) => { return { - ...createAxes({xScale, yScale}, -WIDTH + MARGIN.right), - yTitle: title + ...createAxes({xScale, yScale}, -layout.width + MARGIN.right), + layout: layout, + yTitle: plotYLabel }; } ); -function appendAxes(elem, {xAxis, yAxis, yTitle}) { +function appendAxes(elem, {xAxis, yAxis, layout, yTitle}) { const xLoc = { x: 0, - y: HEIGHT - (MARGIN.top + MARGIN.bottom) + y: layout.height - (MARGIN.top + MARGIN.bottom) }; const yLoc = {x: 0, y: 0}; const yLabelLoc = { - x: HEIGHT / -2 + MARGIN.top, + x: layout.height / -2 + MARGIN.top, y: -35 }; @@ -95,7 +97,6 @@ function appendAxes(elem, {xAxis, yAxis, yTitle}) { .attr('transform', 'rotate(-90)') .attr('x', yLabelLoc.x) .attr('y', yLabelLoc.y) - .attr('dy', '0.71em') .text(yTitle); } diff --git a/assets/src/scripts/components/hydrograph/axes.spec.js b/assets/src/scripts/components/hydrograph/axes.spec.js index 0ecee414e036c3b5ecb051843af8bbe13e1fc997..fd7e0030d49ba2bf9dc88c3d9442086389577b23 100644 --- a/assets/src/scripts/components/hydrograph/axes.spec.js +++ b/assets/src/scripts/components/hydrograph/axes.spec.js @@ -8,6 +8,7 @@ describe('Chart axes', () => { // xScale is oriented on the left const xScale = scaleLinear().range([0, 10]).domain([0, 10]); const yScale = scaleLinear().range([0, 10]).domain([0, 10]); + const layout = {width: 400, height: 200}; const {xAxis, yAxis} = createAxes({xScale, yScale}, 100); let svg; @@ -16,6 +17,7 @@ describe('Chart axes', () => { appendAxes(svg, { xAxis, yAxis, + layout, yTitle: 'Label title' }); }); diff --git a/assets/src/scripts/components/hydrograph/index.js b/assets/src/scripts/components/hydrograph/index.js index 2eadd645890dfeb3196bdde01f6f93e7b2a82c5f..f6fa491af2b75e8ebc6bff80d5eff160f7883f78 100644 --- a/assets/src/scripts/components/hydrograph/index.js +++ b/assets/src/scripts/components/hydrograph/index.js @@ -10,7 +10,7 @@ const { addSVGAccessibility, addSROnlyTable } = require('../../accessibility'); const { dispatch, link, provide } = require('../../lib/redux'); const { appendAxes, axesSelector } = require('./axes'); -const { WIDTH, HEIGHT, ASPECT_RATIO_PERCENT, MARGIN, CIRCLE_RADIUS } = require('./layout'); +const { ASPECT_RATIO_PERCENT, MARGIN, CIRCLE_RADIUS, layoutSelector } = require('./layout'); const { pointsSelector, lineSegmentsSelector, isVisibleSelector } = require('./points'); const { xScaleSelector, yScaleSelector } = require('./scales'); const { Actions, configureStore } = require('./store'); @@ -96,8 +96,8 @@ const plotTooltips = function (elem, {xScale, yScale, data}) { elem.append('rect') .attr('class', 'overlay') - .attr('width', WIDTH) - .attr('height', HEIGHT) + .attr('width', '100%') + .attr('height', '100%') .on('mouseover', () => focus.style('display', null)) .on('mouseout', () => focus.style('display', 'none')) .on('mousemove', function () { @@ -211,6 +211,7 @@ const timeSeriesGraph = function (elem) { .call(link(plotTooltips, createStructuredSelector({ xScale: xScaleSelector('current'), yScale: yScaleSelector, + layout: layoutSelector, data: pointsSelector('current') }))) .call(link(plotMedianPoints, createStructuredSelector({ @@ -250,6 +251,7 @@ const attachToNode = function (node, {siteno} = {}) { let store = configureStore(); + store.dispatch(Actions.resizeTimeseriesPlot(node.offsetWidth)); select(node) .call(provide(store)) .call(timeSeriesGraph) @@ -258,6 +260,10 @@ const attachToNode = function (node, {siteno} = {}) { store.dispatch(Actions.toggleTimeseries('compare', this.checked)); store.dispatch(Actions.selectLegendMarkers()); }); + + window.onresize = function() { + store.dispatch(Actions.resizeTimeseriesPlot(node.offsetWidth)); + }; store.dispatch(Actions.retrieveTimeseries(siteno)); }; diff --git a/assets/src/scripts/components/hydrograph/layout.js b/assets/src/scripts/components/hydrograph/layout.js index f7697cd991c613787065c9ca3090a411fdf875a0..891dcba243fc0deefbae0761a02520accf65c04c 100644 --- a/assets/src/scripts/components/hydrograph/layout.js +++ b/assets/src/scripts/components/hydrograph/layout.js @@ -1,12 +1,31 @@ -// Define width, height and margin for the SVG. -// Use a fixed size, and scale to device width using CSS. -export const WIDTH = 800; -export const HEIGHT = WIDTH / 2; -export const ASPECT_RATIO_PERCENT = `${100 * HEIGHT / WIDTH}%`; -export const MARGIN = { +// Define constants for the timeseries graph's aspect ratio and margins as well as a +// selector function which will return the width/height to use. +const { createSelector } = require('reselect'); + +const ASPECT_RATIO = 1 / 2; +const ASPECT_RATIO_PERCENT = `${100 * ASPECT_RATIO}%`; +const MARGIN = { top: 20, right: 75, bottom: 50, left: 50 }; -export const CIRCLE_RADIUS = 4; \ No newline at end of file +export const CIRCLE_RADIUS = 4; + +/* + * @param {Object} state - Redux store + * @return {Object} containing width and height properties. + */ +const layoutSelector = createSelector( + (state) => state.width, + (width) => { + return { + width: width, + height: width * ASPECT_RATIO + }; + } +); + +module.exports = {ASPECT_RATIO, ASPECT_RATIO_PERCENT, MARGIN, layoutSelector}; + + diff --git a/assets/src/scripts/components/hydrograph/layout.spec.js b/assets/src/scripts/components/hydrograph/layout.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..6aea429fcded9c091747e0372db11767b66fd3dc --- /dev/null +++ b/assets/src/scripts/components/hydrograph/layout.spec.js @@ -0,0 +1,11 @@ + +const { layoutSelector, ASPECT_RATIO } = require('./layout'); + +describe('points module', () => { + it('Should return the width and height with the predefined ASPECT_RATIO', () => { + let layout = layoutSelector({width: 200}); + expect(layout.width).toEqual(200); + expect(layout.height).toEqual(200 * ASPECT_RATIO); + }); +}); + diff --git a/assets/src/scripts/components/hydrograph/scales.js b/assets/src/scripts/components/hydrograph/scales.js index 0ca92bb8fd2436ab0fb2834e508ff5716832ddad..24596cc4e79d05401acc7e2ebf7992317cf50543 100644 --- a/assets/src/scripts/components/hydrograph/scales.js +++ b/assets/src/scripts/components/hydrograph/scales.js @@ -2,7 +2,7 @@ const { extent } = require('d3-array'); const { scaleLinear, scaleTime } = require('d3-scale'); const { createSelector, defaultMemoize: memoize } = require('reselect'); -const { WIDTH, HEIGHT, MARGIN } = require('./layout'); +const { layoutSelector, MARGIN } = require('./layout'); const paddingRatio = 0.2; @@ -35,7 +35,8 @@ function createXScale(values, xSize) { /** * Create an yscale oriented on the bottom - * @param {Array} tsData - Array contains {value, ...} + * @param {Array} tsData - where values are Array contains {value, ...} + * @param {Object} showSeries - keys match keys in tsData and values are Boolean * @param {Number} ySize - range of scale * @eturn {Object} d3 scale for value. */ @@ -83,10 +84,11 @@ function createYScale(tsData, showSeries, ySize) { * @return {Function} D3 scale function */ const xScaleSelector = memoize(tsDataKey => createSelector( + layoutSelector, (state) => state.tsData, - (tsData) => { + (layout, tsData) => { if (tsData[tsDataKey]) { - return createXScale(tsData[tsDataKey], WIDTH - MARGIN.right); + return createXScale(tsData[tsDataKey], layout.width - MARGIN.right); } else { return null; } @@ -100,9 +102,10 @@ const xScaleSelector = memoize(tsDataKey => createSelector( * @return {Function} D3 scale function */ const yScaleSelector = createSelector( + layoutSelector, (state) => state.tsData, (state) => state.showSeries, - (tsData, showSeries) => createYScale(tsData, showSeries, HEIGHT - (MARGIN.top + MARGIN.bottom)) + (layout, tsData, showSeries) => createYScale(tsData, showSeries, layout.height - (MARGIN.top + MARGIN.bottom)) ); diff --git a/assets/src/scripts/components/hydrograph/store.js b/assets/src/scripts/components/hydrograph/store.js index b6352fe371ebdf39d6a85e1c4e58b4835453f540..2c9edbad5e2dd258d003accb44ffa1d413150597 100644 --- a/assets/src/scripts/components/hydrograph/store.js +++ b/assets/src/scripts/components/hydrograph/store.js @@ -78,6 +78,12 @@ export const Actions = { medianStatistics }; }, + resizeTimeseriesPlot(width) { + return { + type: 'RESIZE_TIMESERIES_PLOT', + width + }; + }, setLegendMarkers(key) { return { type: 'SET_LEGEND_MARKERS', @@ -110,6 +116,7 @@ export const timeSeriesReducer = function (state={}, action) { [action.key]: action.show }, title: variableName, + plotYLabel: action.data.variableDescription, desc: action.data.variableDescription + ' from ' + formatTime(action.data.seriesStartDate) + ' to ' + formatTime(action.data.seriesEndDate) @@ -214,6 +221,12 @@ export const timeSeriesReducer = function (state={}, action) { displayMarkers: displayMarkers }; + case 'RESIZE_TIMESERIES_PLOT': + return { + ...state, + width: action.width + }; + default: return state; } @@ -243,6 +256,7 @@ export const configureStore = function (initialState) { displayMarkers: [], title: '', desc: '', + width: 800, ...initialState }; diff --git a/assets/src/styles/components/_hydrograph.scss b/assets/src/styles/components/_hydrograph.scss index b5ef5bf78637e672b5afc7674417e81fe2b1b009..bfff7edd218cb8c8ea549c1c4730eeee31a3c56f 100644 --- a/assets/src/styles/components/_hydrograph.scss +++ b/assets/src/styles/components/_hydrograph.scss @@ -6,7 +6,7 @@ overflow: hidden; svg { - font-size: 2em; + font-size: 0.7em; @include media($small-screen) { font-size: 1em; } @@ -50,14 +50,21 @@ fill: black; text-anchor: middle; } - } - .tick { - line { - fill: none; - stroke: lightgrey; - stroke-opacity: 0.7; + .tick { + line { + fill: none; + stroke: lightgrey; + stroke-opacity: 0.7; + } } - text { + } + .x-axis { + .tick { + line { + fill: none; + stroke: black; + stroke-opacity: 0.7; + } } } .median-data-series {