Merge pull request #278 from acelaya-forks/feature/fix-default-grouping

Feature/fix default grouping
This commit is contained in:
Alejandro Celaya 2020-05-31 20:30:32 +02:00 committed by GitHub
commit 16ffbcfbc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 70 additions and 24 deletions

View file

@ -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

View file

@ -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';

View file

@ -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 = {

View file

@ -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';
@ -52,6 +52,18 @@ const STEP_TO_DATE_FORMAT = {
monthly: (date) => moment(date).format('YYYY-MM'),
};
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
]);
return matcher() || 'monthly';
};
const groupVisitsByStep = (step, visits) => visits.reduce((acc, visit) => {
const key = STEP_TO_DATE_FORMAT[step](visit.date);
@ -93,7 +105,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 ]);

View file

@ -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 = {

View file

@ -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('<VisitStats />', () => {

View file

@ -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('<GraphCard />', () => {
let wrapper;

View file

@ -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('<LineChartCard />', () => {
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', () => {

View file

@ -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('<SortableBarGraph />', () => {
let wrapper;