From e00574553fda86f0cb964ee2f80e4fd904113022 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 31 May 2020 17:51:52 +0200 Subject: [PATCH 1/4] Moved some helper components for visits to visits/helpers --- src/visits/VisitsStats.js | 4 ++-- src/visits/{ => helpers}/GraphCard.js | 2 +- src/visits/{ => helpers}/GraphCard.scss | 0 src/visits/{ => helpers}/SortableBarGraph.js | 10 +++++----- test/visits/VisitsStats.test.js | 4 ++-- test/visits/{ => helpers}/GraphCard.test.js | 2 +- test/visits/{ => helpers}/SortableBarGraph.test.js | 10 +++++----- 7 files changed, 16 insertions(+), 16 deletions(-) rename src/visits/{ => helpers}/GraphCard.js (98%) rename src/visits/{ => helpers}/GraphCard.scss (100%) rename src/visits/{ => helpers}/SortableBarGraph.js (94%) rename test/visits/{ => helpers}/GraphCard.test.js (97%) rename test/visits/{ => helpers}/SortableBarGraph.test.js (91%) diff --git a/src/visits/VisitsStats.js b/src/visits/VisitsStats.js index 7802c98a..c30b8aa2 100644 --- a/src/visits/VisitsStats.js +++ b/src/visits/VisitsStats.js @@ -9,8 +9,8 @@ import DateRangeRow from '../utils/DateRangeRow'; import Message from '../utils/Message'; import { formatDate } from '../utils/helpers/date'; import { useToggle } from '../utils/helpers/hooks'; -import SortableBarGraph from './SortableBarGraph'; -import GraphCard from './GraphCard'; +import SortableBarGraph from './helpers/SortableBarGraph'; +import GraphCard from './helpers/GraphCard'; import LineChartCard from './helpers/LineChartCard'; import VisitsTable from './VisitsTable'; import { VisitsInfoType } from './types'; diff --git a/src/visits/GraphCard.js b/src/visits/helpers/GraphCard.js similarity index 98% rename from src/visits/GraphCard.js rename to src/visits/helpers/GraphCard.js index 9f06c2b7..155a3d9d 100644 --- a/src/visits/GraphCard.js +++ b/src/visits/helpers/GraphCard.js @@ -3,7 +3,7 @@ import { Doughnut, HorizontalBar } from 'react-chartjs-2'; import PropTypes from 'prop-types'; import React from 'react'; import { keys, values } from 'ramda'; -import { fillTheGaps } from '../utils/helpers/visits'; +import { fillTheGaps } from '../../utils/helpers/visits'; import './GraphCard.scss'; const propTypes = { diff --git a/src/visits/GraphCard.scss b/src/visits/helpers/GraphCard.scss similarity index 100% rename from src/visits/GraphCard.scss rename to src/visits/helpers/GraphCard.scss diff --git a/src/visits/SortableBarGraph.js b/src/visits/helpers/SortableBarGraph.js similarity index 94% rename from src/visits/SortableBarGraph.js rename to src/visits/helpers/SortableBarGraph.js index dc64e71f..efaa03f0 100644 --- a/src/visits/SortableBarGraph.js +++ b/src/visits/helpers/SortableBarGraph.js @@ -1,11 +1,11 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { fromPairs, head, keys, pipe, prop, reverse, sortBy, splitEvery, toLower, toPairs, type, zipObj } from 'ramda'; -import SortingDropdown from '../utils/SortingDropdown'; -import PaginationDropdown from '../utils/PaginationDropdown'; -import { rangeOf } from '../utils/utils'; -import { roundTen } from '../utils/helpers/numbers'; -import SimplePaginator from '../common/SimplePaginator'; +import SortingDropdown from '../../utils/SortingDropdown'; +import PaginationDropdown from '../../utils/PaginationDropdown'; +import { rangeOf } from '../../utils/utils'; +import { roundTen } from '../../utils/helpers/numbers'; +import SimplePaginator from '../../common/SimplePaginator'; import GraphCard from './GraphCard'; const propTypes = { diff --git a/test/visits/VisitsStats.test.js b/test/visits/VisitsStats.test.js index 212127ad..c3872512 100644 --- a/test/visits/VisitsStats.test.js +++ b/test/visits/VisitsStats.test.js @@ -4,8 +4,8 @@ import { identity } from 'ramda'; import { Card, Progress } from 'reactstrap'; import createVisitStats from '../../src/visits/VisitsStats'; import Message from '../../src/utils/Message'; -import GraphCard from '../../src/visits/GraphCard'; -import SortableBarGraph from '../../src/visits/SortableBarGraph'; +import GraphCard from '../../src/visits/helpers/GraphCard'; +import SortableBarGraph from '../../src/visits/helpers/SortableBarGraph'; import DateRangeRow from '../../src/utils/DateRangeRow'; describe('', () => { diff --git a/test/visits/GraphCard.test.js b/test/visits/helpers/GraphCard.test.js similarity index 97% rename from test/visits/GraphCard.test.js rename to test/visits/helpers/GraphCard.test.js index 25400289..b75ce941 100644 --- a/test/visits/GraphCard.test.js +++ b/test/visits/helpers/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/helpers/GraphCard'; describe('', () => { let wrapper; diff --git a/test/visits/SortableBarGraph.test.js b/test/visits/helpers/SortableBarGraph.test.js similarity index 91% rename from test/visits/SortableBarGraph.test.js rename to test/visits/helpers/SortableBarGraph.test.js index e947d7f8..12f1d09a 100644 --- a/test/visits/SortableBarGraph.test.js +++ b/test/visits/helpers/SortableBarGraph.test.js @@ -1,11 +1,11 @@ import React from 'react'; import { shallow } from 'enzyme'; import { keys, range, values } from 'ramda'; -import SortableBarGraph from '../../src/visits/SortableBarGraph'; -import GraphCard from '../../src/visits/GraphCard'; -import SortingDropdown from '../../src/utils/SortingDropdown'; -import PaginationDropdown from '../../src/utils/PaginationDropdown'; -import { rangeOf } from '../../src/utils/utils'; +import SortableBarGraph from '../../../src/visits/helpers/SortableBarGraph'; +import GraphCard from '../../../src/visits/helpers/GraphCard'; +import SortingDropdown from '../../../src/utils/SortingDropdown'; +import PaginationDropdown from '../../../src/utils/PaginationDropdown'; +import { rangeOf } from '../../../src/utils/utils'; describe('', () => { let wrapper; From 32cc1cc580693b54c03bca196d703b01e9cee568 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 31 May 2020 20:03:59 +0200 Subject: [PATCH 2/4] Improved logic to determine default grouping for line chart based on how old the visits are --- src/visits/helpers/LineChartCard.js | 20 +++++++++++++++++++- test/visits/helpers/LineChartCard.test.js | 21 +++++++++++++++------ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/visits/helpers/LineChartCard.js b/src/visits/helpers/LineChartCard.js index 9da4abe9..85c26999 100644 --- a/src/visits/helpers/LineChartCard.js +++ b/src/visits/helpers/LineChartCard.js @@ -52,6 +52,22 @@ const STEP_TO_DATE_FORMAT = { monthly: (date) => moment(date).format('YYYY-MM'), }; +const determineInitialStep = (oldestVisitDate) => { + const now = moment(); + const oldestDate = moment(oldestVisitDate); + + if (now.diff(oldestDate, 'day') <= 2) { // Less than 2 days + return 'hourly'; + } else if (now.diff(oldestDate, 'month') <= 1) { // Between 2 days and 1 month + return 'daily'; + } else if (now.diff(oldestDate, 'month') <= 6) { // Between 1 and 6 months + return 'weekly'; + } + + // Older than 6 months + return 'monthly'; +}; + const groupVisitsByStep = (step, visits) => visits.reduce((acc, visit) => { const key = STEP_TO_DATE_FORMAT[step](visit.date); @@ -93,7 +109,9 @@ const generateDataset = (stats, label, color) => ({ }); const LineChartCard = ({ title, visits, highlightedVisits, highlightedLabel = 'Selected' }) => { - const [ step, setStep ] = useState('monthly'); + const [ step, setStep ] = useState( + visits.length > 0 ? determineInitialStep(visits[visits.length - 1].date) : 'monthly' + ); const [ skipNoVisits, toggleSkipNoVisits ] = useToggle(true); const groupedVisitsWithGaps = useMemo(() => groupVisitsByStep(step, reverse(visits)), [ step, visits ]); diff --git a/test/visits/helpers/LineChartCard.test.js b/test/visits/helpers/LineChartCard.test.js index ce75e4db..5d692f5b 100644 --- a/test/visits/helpers/LineChartCard.test.js +++ b/test/visits/helpers/LineChartCard.test.js @@ -2,6 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { CardHeader, DropdownItem } from 'reactstrap'; import { Line } from 'react-chartjs-2'; +import moment from 'moment'; import LineChartCard from '../../../src/visits/helpers/LineChartCard'; import Checkbox from '../../../src/utils/Checkbox'; @@ -22,19 +23,27 @@ describe('', () => { expect(header.html()).toContain('Cool title'); }); - it('renders group menu and selects active grouping item', () => { - const wrapper = createWrapper(); + it.each([ + [[], 'monthly' ], + [[{ date: moment().subtract(1, 'day').format() }], 'hourly' ], + [[{ date: moment().subtract(3, 'day').format() }], 'daily' ], + [[{ date: moment().subtract(2, 'month').format() }], 'weekly' ], + [[{ date: moment().subtract(6, 'month').format() }], 'weekly' ], + [[{ date: moment().subtract(7, 'month').format() }], 'monthly' ], + [[{ date: moment().subtract(1, 'year').format() }], 'monthly' ], + ])('renders group menu and selects proper grouping item based on visits dates', (visits, expectedActiveItem) => { + const wrapper = createWrapper(visits); const items = wrapper.find(DropdownItem); expect(items).toHaveLength(4); expect(items.at(0).prop('children')).toEqual('Month'); - expect(items.at(0).prop('active')).toEqual(true); + expect(items.at(0).prop('active')).toEqual(expectedActiveItem === 'monthly'); expect(items.at(1).prop('children')).toEqual('Week'); - expect(items.at(1).prop('active')).toEqual(false); + expect(items.at(1).prop('active')).toEqual(expectedActiveItem === 'weekly'); expect(items.at(2).prop('children')).toEqual('Day'); - expect(items.at(2).prop('active')).toEqual(false); + expect(items.at(2).prop('active')).toEqual(expectedActiveItem === 'daily'); expect(items.at(3).prop('children')).toEqual('Hour'); - expect(items.at(3).prop('active')).toEqual(false); + expect(items.at(3).prop('active')).toEqual(expectedActiveItem === 'hourly'); }); it('renders chart with expected options', () => { From 73e55cc742c14969e2feed1a4927934ee8ddcbb1 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 31 May 2020 20:16:15 +0200 Subject: [PATCH 3/4] Replaced if/else by functional matcher --- src/visits/helpers/LineChartCard.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/visits/helpers/LineChartCard.js b/src/visits/helpers/LineChartCard.js index 85c26999..d3f3593e 100644 --- a/src/visits/helpers/LineChartCard.js +++ b/src/visits/helpers/LineChartCard.js @@ -10,7 +10,7 @@ import { DropdownItem, } from 'reactstrap'; import { Line } from 'react-chartjs-2'; -import { reverse } from 'ramda'; +import { always, cond, reverse } from 'ramda'; import moment from 'moment'; import { VisitType } from '../types'; import { fillTheGaps } from '../../utils/helpers/visits'; @@ -55,17 +55,13 @@ const STEP_TO_DATE_FORMAT = { const determineInitialStep = (oldestVisitDate) => { const now = moment(); const oldestDate = moment(oldestVisitDate); + const matcher = cond([ + [ () => now.diff(oldestDate, 'day') <= 2, always('hourly') ], // Less than 2 days + [ () => now.diff(oldestDate, 'month') <= 1, always('daily') ], // Between 2 days and 1 month + [ () => now.diff(oldestDate, 'month') <= 6, always('weekly') ], // Between 1 and 6 months + ]); - if (now.diff(oldestDate, 'day') <= 2) { // Less than 2 days - return 'hourly'; - } else if (now.diff(oldestDate, 'month') <= 1) { // Between 2 days and 1 month - return 'daily'; - } else if (now.diff(oldestDate, 'month') <= 6) { // Between 1 and 6 months - return 'weekly'; - } - - // Older than 6 months - return 'monthly'; + return matcher() || 'monthly'; }; const groupVisitsByStep = (step, visits) => visits.reduce((acc, visit) => { From d825b6e174c75928b6214d6102e760ba7efa21e5 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 31 May 2020 20:17:59 +0200 Subject: [PATCH 4/4] Updated changelog --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b17056d3..29096301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org). +## [Unreleased] + +#### Added + +* *Nothing* + +#### Changed + +* *Nothing* + +#### Deprecated + +* *Nothing* + +#### Removed + +* *Nothing* + +#### Fixed + +* [#276](https://github.com/shlinkio/shlink-web-client/issues/276) Fixed default grouping used for visits line chart, making it be dynamic depending on how old the short URL is. + + ## 2.5.0 - 2020-05-31 #### Added