From f5cc1abe7596ab4fe7795e3026813865d8724f96 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 4 Apr 2020 20:16:20 +0200 Subject: [PATCH] Ensured info for selected visit in visits table gets highlighted in bar charts --- src/visits/GraphCard.js | 35 ++++++++++++++++++++++++++-------- src/visits/ShortUrlVisits.js | 18 +++++++++++++---- src/visits/SortableBarGraph.js | 14 ++++++++++++-- src/visits/VisitsTable.js | 2 +- test/visits/GraphCard.test.js | 2 ++ 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/visits/GraphCard.js b/src/visits/GraphCard.js index 253f4011..d9be74cc 100644 --- a/src/visits/GraphCard.js +++ b/src/visits/GraphCard.js @@ -2,7 +2,7 @@ import { Card, CardHeader, CardBody, CardFooter } from 'reactstrap'; import { Doughnut, HorizontalBar } from 'react-chartjs-2'; import PropTypes from 'prop-types'; import React from 'react'; -import { keys, values } from 'ramda'; +import { keys, values, zipObj } from 'ramda'; import './GraphCard.scss'; const propTypes = { @@ -11,9 +11,10 @@ const propTypes = { isBarChart: PropTypes.bool, stats: PropTypes.object, max: PropTypes.number, + highlightedStats: PropTypes.object, }; -const generateGraphData = (title, isBarChart, labels, data) => ({ +const generateGraphData = (title, isBarChart, labels, data, highlightedData) => ({ labels, datasets: [ { @@ -31,23 +32,41 @@ const generateGraphData = (title, isBarChart, labels, data) => ({ borderColor: isBarChart ? 'rgba(70, 150, 229, 1)' : 'white', borderWidth: 2, }, - ], + highlightedData && { + title, + label: 'Selected', + data: highlightedData, + backgroundColor: 'rgba(247, 127, 40, 0.4)', + borderColor: '#F77F28', + borderWidth: 2, + }, + ].filter(Boolean), }); const dropLabelIfHidden = (label) => label.startsWith('hidden') ? '' : label; -const renderGraph = (title, isBarChart, stats, max) => { +const renderGraph = (title, isBarChart, stats, max, highlightedStats) => { const Component = isBarChart ? HorizontalBar : Doughnut; const labels = keys(stats).map(dropLabelIfHidden); - const data = values(stats); + const data = values(!highlightedStats ? stats : keys(highlightedStats).reduce((acc, highlightedKey) => { + if (acc[highlightedKey]) { + acc[highlightedKey] -= 1; + } + + return acc; + }, stats)); + const highlightedData = highlightedStats && values({ ...zipObj(labels, labels.map(() => 0)), ...highlightedStats }); + const options = { legend: isBarChart ? { display: false } : { position: 'right' }, scales: isBarChart && { xAxes: [ { ticks: { beginAtZero: true, max }, + stacked: true, }, ], + yAxes: [{ stacked: true }], }, tooltips: { intersect: !isBarChart, @@ -56,17 +75,17 @@ const renderGraph = (title, isBarChart, stats, max) => { filter: ({ yLabel }) => !isBarChart || yLabel !== '', }, }; - const graphData = generateGraphData(title, isBarChart, labels, data); + const graphData = generateGraphData(title, isBarChart, labels, data, highlightedData); const height = isBarChart && labels.length > 20 ? labels.length * 8 : null; // Provide a key based on the height, so that every time the dataset changes, a new graph is rendered return ; }; -const GraphCard = ({ title, footer, isBarChart, stats, max }) => ( +const GraphCard = ({ title, footer, isBarChart, stats, max, highlightedStats }) => ( {typeof title === 'function' ? title() : title} - {renderGraph(title, isBarChart, stats, max)} + {renderGraph(title, isBarChart, stats, max, highlightedStats)} {footer && {footer}} ); diff --git a/src/visits/ShortUrlVisits.js b/src/visits/ShortUrlVisits.js index 5a5ed3ec..25cbe5f8 100644 --- a/src/visits/ShortUrlVisits.js +++ b/src/visits/ShortUrlVisits.js @@ -15,6 +15,8 @@ import GraphCard from './GraphCard'; import { shortUrlDetailType } from './reducers/shortUrlDetail'; import VisitsTable from './VisitsTable'; +const highlightedVisitToStats = (highlightedVisit, prop) => highlightedVisit && { [highlightedVisit[prop]]: 1 }; + const ShortUrlVisits = ( { processStatsFromVisits }, OpenMapModalBtn @@ -40,6 +42,7 @@ const ShortUrlVisits = ( showTable: false, tableIsSticky: false, isMobileDevice: false, + highlightedVisit: undefined, }; loadVisits = (loadDetail = false) => { @@ -114,9 +117,10 @@ const ShortUrlVisits = (
mapLocations.length > 0 && @@ -188,7 +194,11 @@ const ShortUrlVisits = ( onEntered={() => this.setState({ tableIsSticky: true })} onExiting={() => this.setState({ tableIsSticky: false })} > - + this.setState({ highlightedVisit })} + /> )} diff --git a/src/visits/SortableBarGraph.js b/src/visits/SortableBarGraph.js index 3a538296..54a1bd9b 100644 --- a/src/visits/SortableBarGraph.js +++ b/src/visits/SortableBarGraph.js @@ -15,6 +15,7 @@ const pickValueFromPair = ([ , value ]) => value; export default class SortableBarGraph extends React.Component { static propTypes = { stats: PropTypes.object.isRequired, + highlightedStats: PropTypes.object, title: PropTypes.string.isRequired, sortingItems: PropTypes.object.isRequired, extraHeaderContent: PropTypes.func, @@ -73,7 +74,7 @@ export default class SortableBarGraph extends React.Component { } render() { - const { stats, sortingItems, title, extraHeaderContent, withPagination = true } = this.props; + const { stats, sortingItems, title, extraHeaderContent, highlightedStats, withPagination = true } = this.props; const { currentPageStats, pagination, max } = this.determineStats(stats, sortingItems); const activeCities = keys(currentPageStats); const computeTitle = () => ( @@ -107,6 +108,15 @@ export default class SortableBarGraph extends React.Component { ); - return ; + return ( + + ); } } diff --git a/src/visits/VisitsTable.js b/src/visits/VisitsTable.js index 6a3c5fb1..4a8e0972 100644 --- a/src/visits/VisitsTable.js +++ b/src/visits/VisitsTable.js @@ -98,7 +98,7 @@ const VisitsTable = ({ visits, onVisitSelected, isSticky = false, matchMedia = w 'visits-table__sticky': isSticky, })} > - + Date diff --git a/test/visits/GraphCard.test.js b/test/visits/GraphCard.test.js index af4e1e97..52975c61 100644 --- a/test/visits/GraphCard.test.js +++ b/test/visits/GraphCard.test.js @@ -59,8 +59,10 @@ describe('', () => { xAxes: [ { ticks: { beginAtZero: true }, + stacked: true, }, ], + yAxes: [{ stacked: true }], }); }); });