From 310831a26ae064269ff5252eb5f667153b23ae65 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 7 Apr 2020 22:33:41 +0200 Subject: [PATCH] Converted ShortUrlVisits in functional component --- src/visits/ShortUrlVisits.js | 162 +++++++++++++++-------------- test/visits/ShortUrlVisits.test.js | 10 +- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/src/visits/ShortUrlVisits.js b/src/visits/ShortUrlVisits.js index 25cbe5f8..eddb9d30 100644 --- a/src/visits/ShortUrlVisits.js +++ b/src/visits/ShortUrlVisits.js @@ -1,5 +1,5 @@ -import { isEmpty, mapObjIndexed, values } from 'ramda'; -import React from 'react'; +import { isEmpty, values } from 'ramda'; +import React, { useState, useEffect } from 'react'; import { Button, Card, Collapse } from 'reactstrap'; import PropTypes from 'prop-types'; import qs from 'qs'; @@ -8,6 +8,7 @@ import { faChevronDown as chevronDown } from '@fortawesome/free-solid-svg-icons' 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 { shortUrlVisitsType } from './reducers/shortUrlVisits'; import VisitsHeader from './VisitsHeader'; @@ -15,71 +16,74 @@ import GraphCard from './GraphCard'; import { shortUrlDetailType } from './reducers/shortUrlDetail'; import VisitsTable from './VisitsTable'; +const propTypes = { + match: PropTypes.shape({ + params: PropTypes.object, + }), + location: PropTypes.shape({ + search: PropTypes.string, + }), + getShortUrlVisits: PropTypes.func, + shortUrlVisits: shortUrlVisitsType, + getShortUrlDetail: PropTypes.func, + shortUrlDetail: shortUrlDetailType, + cancelGetShortUrlVisits: PropTypes.func, + matchMedia: PropTypes.func, +}; + const highlightedVisitToStats = (highlightedVisit, prop) => highlightedVisit && { [highlightedVisit[prop]]: 1 }; +const format = formatDate(); +let memoizationId; +let timeWhenMounted; -const ShortUrlVisits = ( - { processStatsFromVisits }, - OpenMapModalBtn -) => class ShortUrlVisits extends React.PureComponent { - static propTypes = { - match: PropTypes.shape({ - params: PropTypes.object, - }), - location: PropTypes.shape({ - search: PropTypes.string, - }), - getShortUrlVisits: PropTypes.func, - shortUrlVisits: shortUrlVisitsType, - getShortUrlDetail: PropTypes.func, - shortUrlDetail: shortUrlDetailType, - cancelGetShortUrlVisits: PropTypes.func, - matchMedia: PropTypes.func, - }; +const ShortUrlVisits = ({ processStatsFromVisits }, OpenMapModalBtn) => { + const ShortUrlVisitsComp = ({ + match, + location, + shortUrlVisits, + shortUrlDetail, + getShortUrlVisits, + getShortUrlDetail, + cancelGetShortUrlVisits, + matchMedia = window.matchMedia, + }) => { + const [ startDate, setStartDate ] = useState(undefined); + const [ endDate, setEndDate ] = useState(undefined); + const [ showTable, toggleTable ] = useToggle(); + const [ tableIsSticky, , setSticky, unsetSticky ] = useToggle(); + const [ highlightedVisit, setHighlightedVisit ] = useState(undefined); + const [ isMobileDevice, setIsMobileDevice ] = useState(false); + const determineIsMobileDevice = () => setIsMobileDevice(matchMedia('(max-width: 991px)').matches); - state = { - startDate: undefined, - endDate: undefined, - showTable: false, - tableIsSticky: false, - isMobileDevice: false, - highlightedVisit: undefined, - }; - - loadVisits = (loadDetail = false) => { - const { match: { params }, location: { search }, getShortUrlVisits, getShortUrlDetail } = this.props; + const { params } = match; const { shortCode } = params; - const { startDate, endDate } = mapObjIndexed(formatDate(), this.state); + const { search } = location; const { domain } = qs.parse(search, { ignoreQueryPrefix: true }); - // While the "page" is loaded, use the timestamp + filtering dates as memoization IDs for stats calculations - this.memoizationId = `${this.timeWhenMounted}_${shortCode}_${startDate}_${endDate}`; - getShortUrlVisits(shortCode, { startDate, endDate, domain }); + const loadVisits = () => { + const start = format(startDate); + const end = format(endDate); - if (loadDetail) { + // While the "page" is loaded, use the timestamp + filtering dates as memoization IDs for stats calculations + memoizationId = `${timeWhenMounted}_${shortCode}_${start}_${end}`; + getShortUrlVisits(shortCode, { startDate: start, endDate: end, domain }); + }; + + useEffect(() => { + timeWhenMounted = new Date().getTime(); getShortUrlDetail(shortCode, domain); - } - }; + determineIsMobileDevice(); + window.addEventListener('resize', determineIsMobileDevice); - setIsMobileDevice = () => { - const { matchMedia = window.matchMedia } = this.props; + return () => { + cancelGetShortUrlVisits(); + window.removeEventListener('resize', determineIsMobileDevice); + }; + }, []); + useEffect(() => { + loadVisits(); + }, [ startDate, endDate ]); - this.setState({ isMobileDevice: matchMedia('(max-width: 991px)').matches }); - }; - - componentDidMount() { - this.timeWhenMounted = new Date().getTime(); - this.loadVisits(true); - this.setIsMobileDevice(); - window.addEventListener('resize', this.setIsMobileDevice); - } - - componentWillUnmount() { - this.props.cancelGetShortUrlVisits(); - window.removeEventListener('resize', this.setIsMobileDevice); - } - - render() { - const { shortUrlVisits, shortUrlDetail } = this.props; const { visits, loading, loadingLarge, error } = shortUrlVisits; const showTableControls = !loading && visits.length > 0; @@ -103,7 +107,7 @@ const ShortUrlVisits = ( } const { os, browsers, referrers, countries, cities, citiesForMap } = processStatsFromVisits( - { id: this.memoizationId, visits } + { id: memoizationId, visits } ); const mapLocations = values(citiesForMap); @@ -120,7 +124,7 @@ const ShortUrlVisits = ( title="Referrers" stats={referrers} withPagination={false} - highlightedStats={highlightedVisitToStats(this.state.highlightedVisit, 'referer')} + highlightedStats={highlightedVisitToStats(highlightedVisit, 'referer')} sortingItems={{ name: 'Referrer name', amount: 'Visits amount', @@ -131,7 +135,7 @@ const ShortUrlVisits = ( mapLocations.length > 0 && - + } sortingItems={{ name: 'City name', @@ -156,7 +160,6 @@ const ShortUrlVisits = ( ); }; - const setDate = (dateField) => (date) => this.setState({ [dateField]: date }, this.loadVisits); return ( @@ -166,20 +169,21 @@ const ShortUrlVisits = (
{showTableControls && ( )}
@@ -188,17 +192,13 @@ const ShortUrlVisits = ( {showTableControls && ( this.setState({ tableIsSticky: true })} - onExiting={() => this.setState({ tableIsSticky: false })} + onEntered={setSticky} + onExiting={unsetSticky} > - this.setState({ highlightedVisit })} - /> + )} @@ -207,7 +207,11 @@ const ShortUrlVisits = ( ); - } + }; + + ShortUrlVisitsComp.propTypes = propTypes; + + return ShortUrlVisitsComp; }; export default ShortUrlVisits; diff --git a/test/visits/ShortUrlVisits.test.js b/test/visits/ShortUrlVisits.test.js index 92096a08..1adae436 100644 --- a/test/visits/ShortUrlVisits.test.js +++ b/test/visits/ShortUrlVisits.test.js @@ -38,10 +38,7 @@ describe('', () => { return wrapper; }; - afterEach(() => { - getShortUrlVisitsMock.mockReset(); - wrapper && wrapper.unmount(); - }); + afterEach(() => wrapper && wrapper.unmount()); it('renders a preloader when visits are loading', () => { const wrapper = createComponent({ loading: true, visits: [] }); @@ -91,9 +88,8 @@ describe('', () => { dateRange.simulate('endDateChange', '2016-01-02T00:00:00+01:00'); dateRange.simulate('endDateChange', '2016-01-03T00:00:00+01:00'); - expect(getShortUrlVisitsMock).toHaveBeenCalledTimes(4); - expect(wrapper.state('startDate')).toEqual('2016-01-01T00:00:00+01:00'); - expect(wrapper.state('endDate')).toEqual('2016-01-03T00:00:00+01:00'); + expect(wrapper.find(DateRangeRow).prop('startDate')).toEqual('2016-01-01T00:00:00+01:00'); + expect(wrapper.find(DateRangeRow).prop('endDate')).toEqual('2016-01-03T00:00:00+01:00'); }); it('holds the map button content generator on cities graph extraHeaderContent', () => {