From dd24a75cfd3f3aa10e65abd184c41e784612f2b3 Mon Sep 17 00:00:00 2001 From: Janell Fry <Janell_Fry@usgs.gov> Date: Fri, 13 Mar 2020 11:17:53 -0700 Subject: [PATCH] IOW-307 Fixing few peer review comments from Mary. --- .../components/dailyValueHydrograph/legend.js | 80 +------------ .../selectors/legend-data.spec.js | 87 ++------------- .../scripts/components/hydrograph/layout.js | 4 +- .../scripts/components/hydrograph/legend.js | 78 +------------ .../components/hydrograph/legend.spec.js | 9 +- assets/src/scripts/d3-rendering/legend.js | 77 +++++++++++++ .../src/scripts/d3-rendering/legend.spec.js | 105 ++++++++++++++++++ assets/src/scripts/index.spec.js | 1 + 8 files changed, 201 insertions(+), 240 deletions(-) create mode 100644 assets/src/scripts/d3-rendering/legend.js create mode 100644 assets/src/scripts/d3-rendering/legend.spec.js diff --git a/assets/src/scripts/components/dailyValueHydrograph/legend.js b/assets/src/scripts/components/dailyValueHydrograph/legend.js index f957ae84b..39500d89b 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/legend.js +++ b/assets/src/scripts/components/dailyValueHydrograph/legend.js @@ -1,88 +1,10 @@ // functions to facilitate DV legend creation for a d3 plot import {createStructuredSelector} from 'reselect'; - import {getLayout} from './selectors/layout'; import {getLegendMarkerRows} from './selectors/legend-data'; - -import config from '../../config'; -import {mediaQuery} from '../../utils'; +import {drawSimpleLegend} from '../../d3-rendering/legend'; import {link} from '../../lib/d3-redux'; - -/** - * Create a simple legend - * - * @param {Object} div - d3 selector where legend should be created - * @param {Object} legendMarkerRows - Array of rows. Each row should be an array of legend markers. - * @param {Object} layout - width and height of svg. - */ -export const drawSimpleLegend = function(div, {legendMarkerRows, layout}) { - div.selectAll('.legend-svg').remove(); - - if (!legendMarkerRows.length) { - return; - } - - const markerGroupXOffset = 15; - const verticalRowOffset = 18; - - let svg = div.append('svg') - .attr('class', 'legend-svg'); - let legend = svg - .append('g') - .attr('class', 'legend') - .attr('transform', `translate(${mediaQuery(config.USWDS_MEDIUM_SCREEN) ? layout.margin.left : 0}, 0)`); - - legendMarkerRows.forEach((rowMarkers, rowIndex) => { - let xPosition = 0; - let yPosition = verticalRowOffset * (rowIndex + 1); - - rowMarkers.forEach((marker) => { - let markerArgs = { - x: xPosition, - y: yPosition, - text: marker.text, - domId: marker.domId, - domClass: marker.domClass, - width: 20, - height: 10, - length: 20, - r: marker.r, - fill: marker.fill - }; - - let markerGroup = marker.type(legend, markerArgs); - let markerGroupBBox; - // Long story short, firefox is unable to get the bounding box if - // the svg element isn't actually taking up space and visible. Folks on the - // internet seem to have gotten around this by setting `visibility:hidden` - // to hide things, but that would still mean the elements will take up space. - // which we don't want. So, here's some error handling for getBBox failures. - // This handling ends up not creating the legend, but that's okay because the - // graph is being shown anyway. A more detailed discussion of this can be found at: - // https://bugzilla.mozilla.org/show_bug.cgi?id=612118 and - // https://stackoverflow.com/questions/28282295/getbbox-of-svg-when-hidden. - try { - markerGroupBBox = markerGroup.node().getBBox(); - xPosition = markerGroupBBox.x + markerGroupBBox.width + markerGroupXOffset; - - } catch(error) { - // See above explanation - } - }); - }); - - // Set the size of the containing svg node to the size of the legend. - let bBox; - try { - bBox = legend.node().getBBox(); - } catch(error) { - return; - } - svg.attr('viewBox', `-4 0 ${layout.width} ${bBox.height + 20}`); -}; - - export const drawTimeSeriesLegend = function(elem, store) { elem.append('div') .classed('hydrograph-container', true) diff --git a/assets/src/scripts/components/dailyValueHydrograph/selectors/legend-data.spec.js b/assets/src/scripts/components/dailyValueHydrograph/selectors/legend-data.spec.js index 0081b66d5..4714c5db4 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/selectors/legend-data.spec.js +++ b/assets/src/scripts/components/dailyValueHydrograph/selectors/legend-data.spec.js @@ -1,7 +1,7 @@ import {select, selectAll} from 'd3-selection'; -import {drawSimpleLegend, drawTimeSeriesLegend} from '../legend'; -import {lineMarker, rectangleMarker, textOnlyMarker} from '../../../d3-rendering/markers'; +import {drawTimeSeriesLegend} from '../legend'; import {getLegendMarkerRows} from './legend-data'; +import {lineMarker, textOnlyMarker} from '../../../d3-rendering/markers'; import {configureStore} from '../../../store'; @@ -26,89 +26,18 @@ describe('DV: Legend module', () => { } } }, + observationsState: { + currentTimeSeriesId: '12345', + showSeries: { + current: true + } + }, ui: { windowWidth: 1024, width: 800 } }; - describe('DV: drawSimpleLegend', () => { - let container; - - const legendMarkerRows = [ - [{ - type: lineMarker, - length: 20, - domId: 'some-id', - domClass: 'some-class', - text: 'Some Text' - }, { - type: rectangleMarker, - domId: 'some-rectangle-id', - domClass: 'some-rectangle-class', - text: 'Rectangle Marker' - }], - [{ - type: textOnlyMarker, - domId: 'text-id', - domClass: 'text-class', - text: 'Label' - }, { - type: lineMarker, - domId: null, - domClass: 'some-other-class', - text: 'Median Label' - }] - ]; - const layout = { - width: 100, - height: 100, - margin: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } - }; - - beforeEach(() => { - container = select('body').append('div'); - }); - - afterEach(() => { - container.remove(); - }); - - it('Does not add a legend svg if no markers are provided', () => { - drawSimpleLegend(container, { - legendMarkerRows: [], - layout: layout - }); - - expect(container.select('svg').size()).toBe(0); - }); - - it('Does not add a legend if the layout is null', () => { - drawSimpleLegend(container, { - legendMarkerRows: legendMarkerRows, - layout: undefined - }); - - expect(container.select('svg').size()).toBe(0); - }); - - it('Adds a legend when width is provided', () => { - drawSimpleLegend(container, {legendMarkerRows, layout}); - - expect(container.select('svg').size()).toBe(1); - expect(container.selectAll('line').size()).toBe(2); - expect(container.selectAll('rect').size()).toBe(1); - expect(container.selectAll('text').size()).toBe(4); - }); - - }); - - describe('DV: legendMarkerRowSelector', () => { it('Should return no markers if no time series to show', () => { diff --git a/assets/src/scripts/components/hydrograph/layout.js b/assets/src/scripts/components/hydrograph/layout.js index 5f1adcd50..a286966a8 100644 --- a/assets/src/scripts/components/hydrograph/layout.js +++ b/assets/src/scripts/components/hydrograph/layout.js @@ -8,8 +8,6 @@ import config from '../../config'; import { mediaQuery } from '../../utils'; import { tickSelector } from './domain'; - - export const ASPECT_RATIO = 1 / 2; export const ASPECT_RATIO_PERCENT = `${100 * ASPECT_RATIO}%`; const MARGIN = { @@ -24,7 +22,7 @@ const MARGIN_SMALL_DEVICE = { bottom: 10, left: 0 }; -export const CIRCLE_RADIUS = 4; +//export const CIRCLE_RADIUS = 4; export const CIRCLE_RADIUS_SINGLE_PT = 1; export const BRUSH_HEIGHT = 100; diff --git a/assets/src/scripts/components/hydrograph/legend.js b/assets/src/scripts/components/hydrograph/legend.js index 296b27aa0..e647f11c4 100644 --- a/assets/src/scripts/components/hydrograph/legend.js +++ b/assets/src/scripts/components/hydrograph/legend.js @@ -3,12 +3,12 @@ import {set} from 'd3-collection'; import memoize from 'fast-memoize'; import {createSelector, createStructuredSelector} from 'reselect'; -import {CIRCLE_RADIUS, getMainLayout} from './layout'; +import {CIRCLE_RADIUS, drawSimpleLegend} from '../../d3-rendering/legend'; import {defineLineMarker, defineTextOnlyMarker, defineRectangleMarker} from '../../d3-rendering/markers'; + +import {getMainLayout} from './layout'; import {currentVariableLineSegmentsSelector, HASH_ID, MASK_DESC} from './drawing-data'; -import config from '../../config'; import {getCurrentVariableMedianMetadata} from '../../selectors/median-statistics-selector'; -import {mediaQuery} from '../../utils'; import {link} from '../../lib/d3-redux'; const TS_LABEL = { @@ -105,78 +105,6 @@ const createLegendMarkers = function(displayItems) { return legendMarkers; }; -/** - * Create a simple legend - * - * @param {Object} div - d3 selector where legend should be created - * @param {Object} legendMarkerRows - Array of rows. Each row should be an array of legend markers. - * @param {Object} layout - width and height of svg. - */ -export const drawSimpleLegend = function(div, {legendMarkerRows, layout}) { - div.selectAll('.legend-svg').remove(); - - if (!legendMarkerRows.length || !layout) { - return; - } - - const markerGroupXOffset = 15; - const verticalRowOffset = 18; - - let svg = div.append('svg') - .attr('class', 'legend-svg'); - let legend = svg - .append('g') - .attr('class', 'legend') - .attr('transform', `translate(${mediaQuery(config.USWDS_MEDIUM_SCREEN) ? layout.margin.left : 0}, 0)`); - - legendMarkerRows.forEach((rowMarkers, rowIndex) => { - let xPosition = 0; - let yPosition = verticalRowOffset * (rowIndex + 1); - - rowMarkers.forEach((marker) => { - let markerArgs = { - x: xPosition, - y: yPosition, - text: marker.text, - domId: marker.domId, - domClass: marker.domClass, - width: 20, - height: 10, - length: 20, - r: marker.r, - fill: marker.fill - }; - - let markerGroup = marker.type(legend, markerArgs); - let markerGroupBBox; - // Long story short, firefox is unable to get the bounding box if - // the svg element isn't actually taking up space and visible. Folks on the - // internet seem to have gotten around this by setting `visibility:hidden` - // to hide things, but that would still mean the elements will take up space. - // which we don't want. So, here's some error handling for getBBox failures. - // This handling ends up not creating the legend, but that's okay because the - // graph is being shown anyway. A more detailed discussion of this can be found at: - // https://bugzilla.mozilla.org/show_bug.cgi?id=612118 and - // https://stackoverflow.com/questions/28282295/getbbox-of-svg-when-hidden. - try { - markerGroupBBox = markerGroup.node().getBBox(); - xPosition = markerGroupBBox.x + markerGroupBBox.width + markerGroupXOffset; - - } catch(error) { - // See above explanation - } - }); - }); - - // Set the size of the containing svg node to the size of the legend. - let bBox; - try { - bBox = legend.node().getBBox(); - } catch(error) { - return; - } - svg.attr('viewBox', `-${CIRCLE_RADIUS} 0 ${layout.width} ${bBox.height + 10}`); -}; const uniqueClassesSelector = memoize(tsKey => createSelector( currentVariableLineSegmentsSelector(tsKey), diff --git a/assets/src/scripts/components/hydrograph/legend.spec.js b/assets/src/scripts/components/hydrograph/legend.spec.js index 7e97f2373..59988d89e 100644 --- a/assets/src/scripts/components/hydrograph/legend.spec.js +++ b/assets/src/scripts/components/hydrograph/legend.spec.js @@ -1,9 +1,10 @@ -import { select, selectAll } from 'd3-selection'; -import { drawSimpleLegend, legendMarkerRowsSelector, drawTimeSeriesLegend } from './legend'; -import { lineMarker, rectangleMarker, textOnlyMarker } from '../../d3-rendering/markers'; +import {select, selectAll} from 'd3-selection'; +import {legendMarkerRowsSelector, drawTimeSeriesLegend} from './legend'; +import {drawSimpleLegend} from '../../d3-rendering/legend'; +import {lineMarker, rectangleMarker, textOnlyMarker} from '../../d3-rendering/markers'; import {Actions, configureStore} from '../../store'; -describe('Legend module', () => { +describe('UV: Legend module', () => { const TEST_DATA = { series: { diff --git a/assets/src/scripts/d3-rendering/legend.js b/assets/src/scripts/d3-rendering/legend.js new file mode 100644 index 000000000..dbac0f5f4 --- /dev/null +++ b/assets/src/scripts/d3-rendering/legend.js @@ -0,0 +1,77 @@ +import {mediaQuery} from "../utils"; +import config from "../config"; + +export const CIRCLE_RADIUS = 4; + +/** + * Create a simple legend + * + * @param {Object} div - d3 selector where legend should be created + * @param {Object} legendMarkerRows - Array of rows. Each row should be an array of legend markers. + * @param {Object} layout - width and height of svg. + */ +export const drawSimpleLegend = function(div, {legendMarkerRows, layout}) { + div.selectAll('.legend-svg').remove(); + + if (!legendMarkerRows.length || !layout) { + return; + } + + const markerGroupXOffset = 15; + const verticalRowOffset = 18; + + let svg = div.append('svg') + .attr('class', 'legend-svg'); + let legend = svg + .append('g') + .attr('class', 'legend') + .attr('transform', `translate(${mediaQuery(config.USWDS_MEDIUM_SCREEN) ? layout.margin.left : 0}, 0)`); + + legendMarkerRows.forEach((rowMarkers, rowIndex) => { + let xPosition = 0; + let yPosition = verticalRowOffset * (rowIndex + 1); + + rowMarkers.forEach((marker) => { + let markerArgs = { + x: xPosition, + y: yPosition, + text: marker.text, + domId: marker.domId, + domClass: marker.domClass, + width: 20, + height: 10, + length: 20, + r: marker.r, + fill: marker.fill + }; + + let markerGroup = marker.type(legend, markerArgs); + let markerGroupBBox; + // Long story short, firefox is unable to get the bounding box if + // the svg element isn't actually taking up space and visible. Folks on the + // internet seem to have gotten around this by setting `visibility:hidden` + // to hide things, but that would still mean the elements will take up space. + // which we don't want. So, here's some error handling for getBBox failures. + // This handling ends up not creating the legend, but that's okay because the + // graph is being shown anyway. A more detailed discussion of this can be found at: + // https://bugzilla.mozilla.org/show_bug.cgi?id=612118 and + // https://stackoverflow.com/questions/28282295/getbbox-of-svg-when-hidden. + try { + markerGroupBBox = markerGroup.node().getBBox(); + xPosition = markerGroupBBox.x + markerGroupBBox.width + markerGroupXOffset; + + } catch(error) { + // See above explanation + } + }); + }); + + // Set the size of the containing svg node to the size of the legend. + let bBox; + try { + bBox = legend.node().getBBox(); + } catch(error) { + return; + } + svg.attr('viewBox', `-${CIRCLE_RADIUS} 0 ${layout.width} ${bBox.height + 10}`); +}; \ No newline at end of file diff --git a/assets/src/scripts/d3-rendering/legend.spec.js b/assets/src/scripts/d3-rendering/legend.spec.js new file mode 100644 index 000000000..33aea5c10 --- /dev/null +++ b/assets/src/scripts/d3-rendering/legend.spec.js @@ -0,0 +1,105 @@ +import {select, selectAll} from 'd3-selection'; +import {lineMarker, rectangleMarker, textOnlyMarker} from "./markers"; +import {drawSimpleLegend} from "./legend"; + +describe('D3-rendering: Legend module', () => { + + const TEST_STATE = { + observationsData: { + timeSeries: { + '12345': { + type: 'Feature', + id: '12345', + properties: { + phenomenonTimeStart: '2018-01-02', + phenomenonTimeEnd: '2018-01-05', + timeStep: ['2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05'], + result: ['5.0', '4.0', '6.1', '3.2'], + approvals: [['Approved'], ['Approved'], ['Approved'], ['Estimated']], + nilReason: [null, 'AA', null, null], + qualifiers: [null, null, ['ICE'], ['ICE']], + grades: [['50'], ['50'], ['60'], ['60']] + } + } + } + }, + observationsState: { + currentTimeSeriesId: '12345', + showSeries: { + current: true + } + }, + ui: { + windowWidth: 1024, + width: 800 + } + }; + + describe('drawSimpleLegend', () => { + let container; + + const legendMarkerRows = [ + [{ + type: lineMarker, + length: 20, + domId: 'some-id', + domClass: 'some-class', + text: 'Some Text' + }, { + type: rectangleMarker, + domId: 'some-rectangle-id', + domClass: 'some-rectangle-class', + text: 'Rectangle Marker' + }], + [{ + type: textOnlyMarker, + domId: 'text-id', + domClass: 'text-class', + text: 'Label' + }, { + type: lineMarker, + domId: null, + domClass: 'some-other-class', + text: 'Median Label' + }] + ]; + const layout = { + width: 100, + height: 100, + margin: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }; + + beforeEach(() => { + container = select('body').append('div'); + }); + + afterEach(() => { + container.remove(); + }); + + it('Does not add a legend svg if no markers are provided', () => { + drawSimpleLegend(container, { + legendMarkerRows: [], + layout: layout + }); + + expect(container.select('svg').size()).toBe(0); + }); + + it('Adds a legend when width is provided', () => { + drawSimpleLegend(container, {legendMarkerRows, layout}); + + expect(container.select('svg').size()).toBe(1); + expect(container.selectAll('line').size()).toBe(2); + expect(container.selectAll('rect').size()).toBe(1); + expect(container.selectAll('text').size()).toBe(4); + }); + + }); + +}); \ No newline at end of file diff --git a/assets/src/scripts/index.spec.js b/assets/src/scripts/index.spec.js index f65b8fa79..220329bea 100644 --- a/assets/src/scripts/index.spec.js +++ b/assets/src/scripts/index.spec.js @@ -18,6 +18,7 @@ import './d3-rendering/cursor-slider.spec'; import './d3-rendering/loading-indicator.spec'; import './d3-rendering/graph-tooltip.spec'; import './d3-rendering/markers.spec'; +import './d3-rendering/legend.spec'; import './components/dailyValueHydrograph/selectors/labels.spec'; import './components/dailyValueHydrograph/selectors/scales.spec'; -- GitLab