diff --git a/src/visits/GraphCard.js b/src/visits/GraphCard.js index e1a814da..4351fefa 100644 --- a/src/visits/GraphCard.js +++ b/src/visits/GraphCard.js @@ -8,51 +8,86 @@ const propTypes = { title: PropTypes.string, isBarChart: PropTypes.bool, stats: PropTypes.object, + matchMedia: PropTypes.func, +}; +const defaultProps = { + matchMedia: global.window ? global.window.matchMedia : () => {}, }; -export function GraphCard({ title, isBarChart, stats }) { - const generateGraphData = (stats) => ({ - labels: keys(stats), - datasets: [ - { - title, - data: values(stats), - backgroundColor: isBarChart ? 'rgba(70, 150, 229, 0.4)' : [ - '#97BBCD', - '#DCDCDC', - '#F7464A', - '#46BFBD', - '#FDB45C', - '#949FB1', - '#4D5360', - ], - borderColor: isBarChart ? 'rgba(70, 150, 229, 1)' : 'white', - borderWidth: 2, - }, - ], - }); - const renderGraph = () => { - const Component = isBarChart ? HorizontalBar : Doughnut; - const options = { - legend: isBarChart ? { display: false } : { position: 'right' }, - scales: isBarChart ? { - xAxes: [ - { - ticks: { beginAtZero: true }, - }, - ], - } : null, - }; +const generateGraphData = (title, isBarChart, labels, data) => ({ + labels, + datasets: [ + { + title, + data, + backgroundColor: isBarChart ? 'rgba(70, 150, 229, 0.4)' : [ + '#97BBCD', + '#DCDCDC', + '#F7464A', + '#46BFBD', + '#FDB45C', + '#949FB1', + '#4D5360', + ], + borderColor: isBarChart ? 'rgba(70, 150, 229, 1)' : 'white', + borderWidth: 2, + }, + ], +}); - return ; +const determineGraphAspectRatio = (barsCount, isBarChart, matchMedia) => { + const determineAspectRationModifier = () => { + switch (true) { + case matchMedia('(max-width: 1200px)').matches: + return 1.5; // eslint-disable-line no-magic-numbers + case matchMedia('(max-width: 992px)').matches: + return 1.75; // eslint-disable-line no-magic-numbers + case matchMedia('(max-width: 768px)').matches: + return 2; // eslint-disable-line no-magic-numbers + case matchMedia('(max-width: 576px)').matches: + return 2.25; // eslint-disable-line no-magic-numbers + default: + return 1; + } }; - return ( - - {title} - {renderGraph()} - - ); -} + const MAX_BARS_WITHOUT_HEIGHT = 20; + const DEFAULT_ASPECT_RATION = 2; + const shouldCalculateAspectRatio = isBarChart && barsCount > MAX_BARS_WITHOUT_HEIGHT; + + return shouldCalculateAspectRatio + ? MAX_BARS_WITHOUT_HEIGHT / determineAspectRationModifier() * DEFAULT_ASPECT_RATION / barsCount + : DEFAULT_ASPECT_RATION; +}; + +const renderGraph = (title, isBarChart, stats, matchMedia) => { + const Component = isBarChart ? HorizontalBar : Doughnut; + const labels = keys(stats); + const data = values(stats); + const aspectRatio = determineGraphAspectRatio(labels.length, isBarChart, matchMedia); + const options = { + aspectRatio, + legend: isBarChart ? { display: false } : { position: 'right' }, + scales: isBarChart ? { + xAxes: [ + { + ticks: { beginAtZero: true }, + }, + ], + } : null, + }; + + return ; +}; + +const GraphCard = ({ title, isBarChart, stats, matchMedia }) => ( + + {title} + {renderGraph(title, isBarChart, stats, matchMedia)} + +); GraphCard.propTypes = propTypes; +GraphCard.defaultProps = defaultProps; + +export default GraphCard; diff --git a/src/visits/ShortUrlVisits.js b/src/visits/ShortUrlVisits.js index 11256c0e..72d162cb 100644 --- a/src/visits/ShortUrlVisits.js +++ b/src/visits/ShortUrlVisits.js @@ -15,7 +15,7 @@ import { processReferrersStats, } from './services/VisitsParser'; import { VisitsHeader } from './VisitsHeader'; -import { GraphCard } from './GraphCard'; +import GraphCard from './GraphCard'; import { getShortUrlDetail, shortUrlDetailType } from './reducers/shortUrlDetail'; import './ShortUrlVisits.scss'; diff --git a/test/visits/GraphCard.test.js b/test/visits/GraphCard.test.js index 50d2ae14..0525efd0 100644 --- a/test/visits/GraphCard.test.js +++ b/test/visits/GraphCard.test.js @@ -2,7 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Doughnut, HorizontalBar } from 'react-chartjs-2'; import { keys, values } from 'ramda'; -import { GraphCard } from '../../src/visits/GraphCard'; +import GraphCard from '../../src/visits/GraphCard'; describe('', () => { let wrapper; @@ -10,6 +10,7 @@ describe('', () => { foo: 123, bar: 456, }; + const matchMedia = () => ({ matches: false }); afterEach(() => { if (wrapper) { @@ -18,7 +19,7 @@ describe('', () => { }); it('renders Doughnut when is not a bar chart', () => { - wrapper = shallow(); + wrapper = shallow(); const doughnut = wrapper.find(Doughnut); const horizontal = wrapper.find(HorizontalBar); @@ -46,7 +47,7 @@ describe('', () => { }); it('renders HorizontalBar when is not a bar chart', () => { - wrapper = shallow(); + wrapper = shallow(); const doughnut = wrapper.find(Doughnut); const horizontal = wrapper.find(HorizontalBar); diff --git a/test/visits/ShortUrlVisits.test.js b/test/visits/ShortUrlVisits.test.js index d7d2a3e8..56837968 100644 --- a/test/visits/ShortUrlVisits.test.js +++ b/test/visits/ShortUrlVisits.test.js @@ -5,7 +5,7 @@ import { Card } from 'reactstrap'; import * as sinon from 'sinon'; import { ShortUrlsVisitsComponent as ShortUrlsVisits } from '../../src/visits/ShortUrlVisits'; import MutedMessage from '../../src/utils/MuttedMessage'; -import { GraphCard } from '../../src/visits/GraphCard'; +import GraphCard from '../../src/visits/GraphCard'; import DateInput from '../../src/common/DateInput'; describe('', () => {