diff --git a/src/visits/helpers/LineChartCard.js b/src/visits/helpers/LineChartCard.js index b856ee2f..e9afe1db 100644 --- a/src/visits/helpers/LineChartCard.js +++ b/src/visits/helpers/LineChartCard.js @@ -14,7 +14,10 @@ import { reverse } from 'ramda'; import moment from 'moment'; import { VisitType } from '../types'; import { fillTheGaps } from '../../utils/helpers/visits'; -import './LineCHartCard.scss'; +import './LineChartCard.scss'; +import { useToggle } from '../../utils/helpers/hooks'; +import { rangeOf } from '../../utils/utils'; +import Checkbox from '../../utils/Checkbox'; const propTypes = { title: PropTypes.string, @@ -22,12 +25,19 @@ const propTypes = { highlightedVisits: PropTypes.arrayOf(VisitType), }; -const steps = [ - { value: 'monthly', menuText: 'Month' }, - { value: 'weekly', menuText: 'Week' }, - { value: 'daily', menuText: 'Day' }, - { value: 'hourly', menuText: 'Hour' }, -]; +const STEPS_MAP = { + monthly: 'Month', + weekly: 'Week', + daily: 'Day', + hourly: 'Hour', +}; + +const STEP_TO_DIFF_UNIT_MAP = { + hourly: 'hour', + daily: 'day', + weekly: 'week', + monthly: 'month', +}; const STEP_TO_DATE_FORMAT = { hourly: (date) => moment(date).format('YYYY-MM-DD HH:00'), @@ -49,6 +59,33 @@ const groupVisitsByStep = (step, visits) => visits.reduce((acc, visit) => { return acc; }, {}); +const generateLabels = (step, visits) => { + const newerDate = moment(visits[0].date); + const oldestDate = moment(visits[visits.length - 1].date); + const size = newerDate.diff(oldestDate, STEP_TO_DIFF_UNIT_MAP[step]); + + return [ + STEP_TO_DATE_FORMAT[step](oldestDate), + ...rangeOf(size, () => { + const date = oldestDate.add(1, STEP_TO_DIFF_UNIT_MAP[step]); + + return STEP_TO_DATE_FORMAT[step](date); + }), + ]; +}; + +const generateLabelsAndGroupedVisits = (visits, step, skipNoElements) => { + const groupedVisits = groupVisitsByStep(step, reverse(visits)); + + if (skipNoElements) { + return [ Object.keys(groupedVisits), groupedVisits ]; + } + + const labels = generateLabels(step, visits); + + return [ labels, fillTheGaps(groupedVisits, labels) ]; +}; + const generateDataset = (stats, label, color) => ({ label, data: Object.values(stats), @@ -59,9 +96,13 @@ const generateDataset = (stats, label, color) => ({ }); const LineChartCard = ({ title, visits, highlightedVisits }) => { - const [ step, setStep ] = useState(steps[0].value); - const groupedVisits = useMemo(() => groupVisitsByStep(step, reverse(visits)), [ visits, step ]); - const labels = useMemo(() => Object.keys(groupedVisits), [ groupedVisits ]); + const [ step, setStep ] = useState('monthly'); + const [ skipNoVisits, toggleSkipNoVisits ] = useToggle(true); + + const [ labels, groupedVisits ] = useMemo( + () => generateLabelsAndGroupedVisits(visits, step, skipNoVisits), + [ visits, step, skipNoVisits ] + ); const groupedHighlighted = useMemo( () => fillTheGaps(groupVisitsByStep(step, reverse(highlightedVisits)), labels), [ highlightedVisits, step, labels ] @@ -83,6 +124,15 @@ const LineChartCard = ({ title, visits, highlightedVisits }) => { ticks: { beginAtZero: true, precision: 0 }, }, ], + xAxes: [ + { + scaleLabel: { display: true, labelString: STEPS_MAP[step] }, + }, + ], + }, + tooltips: { + intersect: false, + axis: 'x', }, }; @@ -96,7 +146,7 @@ const LineChartCard = ({ title, visits, highlightedVisits }) => { Group by - {steps.map(({ menuText, value }) => ( + {Object.entries(STEPS_MAP).map(([ value, menuText ]) => ( setStep(value)}> {menuText} @@ -104,6 +154,11 @@ const LineChartCard = ({ title, visits, highlightedVisits }) => { +
+ + Skip dates with no visits + +
diff --git a/src/visits/helpers/LineCHartCard.scss b/src/visits/helpers/LineChartCard.scss similarity index 100% rename from src/visits/helpers/LineCHartCard.scss rename to src/visits/helpers/LineChartCard.scss diff --git a/test/visits/helpers/LineChartCard.test.js b/test/visits/helpers/LineChartCard.test.js index 1c6bf376..ce75e4db 100644 --- a/test/visits/helpers/LineChartCard.test.js +++ b/test/visits/helpers/LineChartCard.test.js @@ -3,8 +3,9 @@ import { shallow } from 'enzyme'; import { CardHeader, DropdownItem } from 'reactstrap'; import { Line } from 'react-chartjs-2'; import LineChartCard from '../../../src/visits/helpers/LineChartCard'; +import Checkbox from '../../../src/utils/Checkbox'; -describe('', () => { +describe('', () => { let wrapper; const createWrapper = (visits = [], highlightedVisits = []) => { wrapper = shallow(); @@ -49,6 +50,15 @@ describe('', () => { ticks: { beginAtZero: true, precision: 0 }, }, ], + xAxes: [ + { + scaleLabel: { display: true, labelString: 'Month' }, + }, + ], + }, + tooltips: { + intersect: false, + axis: 'x', }, }); }); @@ -63,4 +73,15 @@ describe('', () => { expect(datasets).toHaveLength(expectedLines); }); + + it('includes stats for visits with no dates if selected', () => { + const wrapper = createWrapper([ + { date: '2016-04-01' }, + { date: '2016-01-01' }, + ]); + + expect(wrapper.find(Line).prop('data').labels).toHaveLength(2); + wrapper.find(Checkbox).simulate('change'); + expect(wrapper.find(Line).prop('data').labels).toHaveLength(4); + }); });