Migrated to TS all visits components except the biggest two

This commit is contained in:
Alejandro Celaya 2020-09-04 19:33:16 +02:00
parent f2e7a2161d
commit 73b854037d
14 changed files with 85 additions and 173 deletions

View file

@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import { Action, Dispatch } from 'redux';
import { ShlinkMercureInfo } from '../../utils/services/types';
import { GetState } from '../../container/types';
@ -11,14 +10,6 @@ export const GET_MERCURE_INFO_ERROR = 'shlink/mercure/GET_MERCURE_INFO_ERROR';
export const GET_MERCURE_INFO = 'shlink/mercure/GET_MERCURE_INFO';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use MercureInfo interface */
export const MercureInfoType = PropTypes.shape({
token: PropTypes.string,
mercureHubUrl: PropTypes.string,
loading: PropTypes.bool,
error: PropTypes.bool,
});
export interface MercureInfo {
token?: string;
mercureHubUrl?: string;

View file

@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import { Dispatch, Action } from 'redux';
import { ShortUrlIdentifier, ShortUrlMeta } from '../data';
import { GetState } from '../../container/types';
@ -13,13 +12,6 @@ export const SHORT_URL_META_EDITED = 'shlink/shortUrlMeta/SHORT_URL_META_EDITED'
export const RESET_EDIT_SHORT_URL_META = 'shlink/shortUrlMeta/RESET_EDIT_SHORT_URL_META';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use ShortUrlMeta interface instead */
export const shortUrlMetaType = PropTypes.shape({
validSince: PropTypes.string,
validUntil: PropTypes.string,
maxVisits: PropTypes.number,
});
export interface ShortUrlMetaEdition {
shortCode: string | null;
meta: ShortUrlMeta;

View file

@ -1,5 +1,4 @@
import { assoc, assocPath, reject } from 'ramda';
import PropTypes from 'prop-types';
import { Action, Dispatch } from 'redux';
import { shortUrlMatches } from '../helpers';
import { CREATE_VISIT, CreateVisitAction } from '../../visits/reducers/visitCreation';
@ -10,7 +9,7 @@ import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuil
import { ShlinkShortUrlsResponse } from '../../utils/services/types';
import { EditShortUrlTagsAction, SHORT_URL_TAGS_EDITED } from './shortUrlTags';
import { SHORT_URL_DELETED } from './shortUrlDeletion';
import { SHORT_URL_META_EDITED, ShortUrlMetaEditedAction, shortUrlMetaType } from './shortUrlMeta';
import { SHORT_URL_META_EDITED, ShortUrlMetaEditedAction } from './shortUrlMeta';
import { SHORT_URL_EDITED, ShortUrlEditedAction } from './shortUrlEdition';
import { ShortUrlsListParams } from './shortUrlsListParams';
@ -20,17 +19,6 @@ export const LIST_SHORT_URLS_ERROR = 'shlink/shortUrlsList/LIST_SHORT_URLS_ERROR
export const LIST_SHORT_URLS = 'shlink/shortUrlsList/LIST_SHORT_URLS';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use ShortUrl interface instead */
export const shortUrlType = PropTypes.shape({
shortCode: PropTypes.string,
shortUrl: PropTypes.string,
longUrl: PropTypes.string,
visitsCount: PropTypes.number,
meta: shortUrlMetaType,
tags: PropTypes.arrayOf(PropTypes.string),
domain: PropTypes.string,
});
export interface ShortUrlsList {
shortUrls?: ShlinkShortUrlsResponse;
loading: boolean;

View file

@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import { rangeOf } from '../utils';
import LocalStorage from './LocalStorage';
@ -36,9 +35,3 @@ export default class ColorGenerator {
return color;
};
}
/** @deprecated Use ColorGenerator class instead */
export const colorGeneratorType = PropTypes.shape({
getColorForKey: PropTypes.func,
setColorForKey: PropTypes.func,
});

View file

@ -7,7 +7,7 @@ import { ShortUrlVisits as ShortUrlVisitsState } from './reducers/shortUrlVisits
import ShortUrlVisitsHeader from './ShortUrlVisitsHeader';
import { ShortUrlDetail } from './reducers/shortUrlDetail';
interface ShortUrlVisitsProps extends RouteComponentProps<{ shortCode: string }>, MercureBoundProps {
export interface ShortUrlVisitsProps extends RouteComponentProps<{ shortCode: string }>, MercureBoundProps {
getShortUrlVisits: (shortCode: string, query?: ShlinkVisitsParams) => void;
shortUrlVisits: ShortUrlVisitsState;
getShortUrlDetail: Function;

View file

@ -1,24 +1,23 @@
import { UncontrolledTooltip } from 'reactstrap';
import Moment from 'react-moment';
import React from 'react';
import PropTypes from 'prop-types';
import { ExternalLink } from 'react-external-link';
import { shortUrlDetailType } from './reducers/shortUrlDetail';
import { shortUrlVisitsType } from './reducers/shortUrlVisits';
import { ShortUrlDetail } from './reducers/shortUrlDetail';
import { ShortUrlVisits } from './reducers/shortUrlVisits';
import VisitsHeader from './VisitsHeader';
import './ShortUrlVisitsHeader.scss';
const propTypes = {
shortUrlDetail: shortUrlDetailType.isRequired,
shortUrlVisits: shortUrlVisitsType.isRequired,
goBack: PropTypes.func.isRequired,
};
interface ShortUrlVisitsHeaderProps {
shortUrlDetail: ShortUrlDetail;
shortUrlVisits: ShortUrlVisits;
goBack: () => void;
}
const ShortUrlVisitsHeader = ({ shortUrlDetail, shortUrlVisits, goBack }) => {
const ShortUrlVisitsHeader = ({ shortUrlDetail, shortUrlVisits, goBack }: ShortUrlVisitsHeaderProps) => {
const { shortUrl, loading } = shortUrlDetail;
const { visits } = shortUrlVisits;
const shortLink = shortUrl && shortUrl.shortUrl ? shortUrl.shortUrl : '';
const longLink = shortUrl && shortUrl.longUrl ? shortUrl.longUrl : '';
const shortLink = shortUrl?.shortUrl ?? '';
const longLink = shortUrl?.longUrl ?? '';
const renderDate = () => !shortUrl ? <small>Loading...</small> : (
<span>
@ -49,6 +48,4 @@ const ShortUrlVisitsHeader = ({ shortUrlDetail, shortUrlVisits, goBack }) => {
);
};
ShortUrlVisitsHeader.propTypes = propTypes;
export default ShortUrlVisitsHeader;

View file

@ -1,52 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { MercureInfoType } from '../mercure/reducers/mercureInfo';
import { useMercureTopicBinding } from '../mercure/helpers';
import { TagVisitsType } from './reducers/tagVisits';
import TagVisitsHeader from './TagVisitsHeader';
const propTypes = {
history: PropTypes.shape({
goBack: PropTypes.func,
}),
match: PropTypes.shape({
params: PropTypes.object,
}),
getTagVisits: PropTypes.func,
tagVisits: TagVisitsType,
cancelGetTagVisits: PropTypes.func,
createNewVisit: PropTypes.func,
loadMercureInfo: PropTypes.func,
mercureInfo: MercureInfoType,
};
const TagVisits = (VisitsStats, colorGenerator) => {
const TagVisitsComp = ({
history,
match,
getTagVisits,
tagVisits,
cancelGetTagVisits,
createNewVisit,
loadMercureInfo,
mercureInfo,
}) => {
const { params } = match;
const { tag } = params;
const loadVisits = (dates) => getTagVisits(tag, dates);
useMercureTopicBinding(mercureInfo, 'https://shlink.io/new-visit', createNewVisit, loadMercureInfo);
return (
<VisitsStats getVisits={loadVisits} cancelGetVisits={cancelGetTagVisits} visitsInfo={tagVisits}>
<TagVisitsHeader tagVisits={tagVisits} goBack={history.goBack} colorGenerator={colorGenerator} />
</VisitsStats>
);
};
TagVisitsComp.propTypes = propTypes;
return TagVisitsComp;
};
export default TagVisits;

37
src/visits/TagVisits.tsx Normal file
View file

@ -0,0 +1,37 @@
import React, { FC } from 'react';
import { RouteComponentProps } from 'react-router';
import { MercureBoundProps, useMercureTopicBinding } from '../mercure/helpers';
import ColorGenerator from '../utils/services/ColorGenerator';
import { TagVisits as TagVisitsState } from './reducers/tagVisits';
import TagVisitsHeader from './TagVisitsHeader';
export interface TagVisitsProps extends RouteComponentProps<{ tag: string }>, MercureBoundProps {
getTagVisits: (tag: string, query: any) => void;
tagVisits: TagVisitsState;
cancelGetTagVisits: Function;
}
const TagVisits = (VisitsStats: FC<any>, colorGenerator: ColorGenerator) => ({ // TODO Use VisitsStatsProps once available
history: { goBack },
match,
getTagVisits,
tagVisits,
cancelGetTagVisits,
createNewVisit,
loadMercureInfo,
mercureInfo,
}: TagVisitsProps) => {
const { params } = match;
const { tag } = params;
const loadVisits = (dates: any) => getTagVisits(tag, dates);
useMercureTopicBinding(mercureInfo, 'https://shlink.io/new-visit', createNewVisit, loadMercureInfo);
return (
<VisitsStats getVisits={loadVisits} cancelGetVisits={cancelGetTagVisits} visitsInfo={tagVisits}>
<TagVisitsHeader tagVisits={tagVisits} goBack={goBack} colorGenerator={colorGenerator} />
</VisitsStats>
);
};
export default TagVisits;

View file

@ -1,18 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import Tag from '../tags/helpers/Tag';
import { colorGeneratorType } from '../utils/services/ColorGenerator';
import ColorGenerator from '../utils/services/ColorGenerator';
import VisitsHeader from './VisitsHeader';
import { TagVisitsType } from './reducers/tagVisits';
import { TagVisits } from './reducers/tagVisits';
import './ShortUrlVisitsHeader.scss';
const propTypes = {
tagVisits: TagVisitsType.isRequired,
goBack: PropTypes.func.isRequired,
colorGenerator: colorGeneratorType,
};
interface TagVisitsHeader {
tagVisits: TagVisits;
goBack: () => void;
colorGenerator: ColorGenerator;
}
const TagVisitsHeader = ({ tagVisits, goBack, colorGenerator }) => {
const TagVisitsHeader = ({ tagVisits, goBack, colorGenerator }: TagVisitsHeader) => {
const { visits, tag } = tagVisits;
const visitsStatsTitle = (
@ -25,6 +24,4 @@ const TagVisitsHeader = ({ tagVisits, goBack, colorGenerator }) => {
return <VisitsHeader title={visitsStatsTitle} goBack={goBack} visits={visits} />;
};
TagVisitsHeader.propTypes = propTypes;
export default TagVisitsHeader;

View file

@ -1,21 +1,19 @@
import { Button, Card } from 'reactstrap';
import React from 'react';
import PropTypes from 'prop-types';
import React, { FC, ReactNode } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import ShortUrlVisitsCount from '../short-urls/helpers/ShortUrlVisitsCount';
import { shortUrlType } from '../short-urls/reducers/shortUrlsList';
import { VisitType } from './types';
import { ShortUrl } from '../short-urls/data';
import { Visit } from './types';
const propTypes = {
visits: PropTypes.arrayOf(VisitType).isRequired,
goBack: PropTypes.func.isRequired,
title: PropTypes.node.isRequired,
children: PropTypes.node,
shortUrl: shortUrlType,
};
interface VisitsHeaderProps {
visits: Visit[];
goBack: () => void;
title: ReactNode;
shortUrl?: ShortUrl;
}
const VisitsHeader = ({ visits, goBack, shortUrl, children, title }) => (
const VisitsHeader: FC<VisitsHeaderProps> = ({ visits, goBack, shortUrl, children, title }) => (
<header>
<Card className="bg-light" body>
<h2 className="d-flex justify-content-between align-items-center mb-0">
@ -39,6 +37,4 @@ const VisitsHeader = ({ visits, goBack, shortUrl, children, title }) => (
</header>
);
VisitsHeader.propTypes = propTypes;
export default VisitsHeader;

View file

@ -1,6 +1,4 @@
import PropTypes from 'prop-types';
import { Action, Dispatch } from 'redux';
import { shortUrlType } from '../../short-urls/reducers/shortUrlsList';
import { ShortUrl } from '../../short-urls/data';
import { buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder';
@ -13,13 +11,6 @@ export const GET_SHORT_URL_DETAIL_ERROR = 'shlink/shortUrlDetail/GET_SHORT_URL_D
export const GET_SHORT_URL_DETAIL = 'shlink/shortUrlDetail/GET_SHORT_URL_DETAIL';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use ShortUrlDetail interface instead */
export const shortUrlDetailType = PropTypes.shape({
shortUrl: shortUrlType,
loading: PropTypes.bool,
error: PropTypes.bool,
});
export interface ShortUrlDetail {
shortUrl?: ShortUrl;
loading: boolean;

View file

@ -1,7 +1,6 @@
import PropTypes from 'prop-types';
import { Action, Dispatch } from 'redux';
import { shortUrlMatches } from '../../short-urls/helpers';
import { Visit, VisitsInfo, VisitsLoadProgressChangedAction, VisitType } from '../types';
import { Visit, VisitsInfo, VisitsLoadProgressChangedAction } from '../types';
import { ShortUrlIdentifier } from '../../short-urls/data';
import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder';
@ -19,17 +18,6 @@ export const GET_SHORT_URL_VISITS_CANCEL = 'shlink/shortUrlVisits/GET_SHORT_URL_
export const GET_SHORT_URL_VISITS_PROGRESS_CHANGED = 'shlink/shortUrlVisits/GET_SHORT_URL_VISITS_PROGRESS_CHANGED';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use ShortUrlVisits interface instead */
export const shortUrlVisitsType = PropTypes.shape({
visits: PropTypes.arrayOf(VisitType),
shortCode: PropTypes.string,
domain: PropTypes.string,
loading: PropTypes.bool,
loadingLarge: PropTypes.bool,
error: PropTypes.bool,
progress: PropTypes.number,
});
export interface ShortUrlVisits extends VisitsInfo, ShortUrlIdentifier {}
interface ShortUrlVisitsAction extends Action<string>, ShortUrlIdentifier {

View file

@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import { Action, Dispatch } from 'redux';
import { Visit, VisitsInfo, VisitsLoadProgressChangedAction, VisitType } from '../types';
import { Visit, VisitsInfo, VisitsLoadProgressChangedAction } from '../types';
import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder';
import { GetState } from '../../container/types';
@ -16,16 +15,6 @@ export const GET_TAG_VISITS_CANCEL = 'shlink/tagVisits/GET_TAG_VISITS_CANCEL';
export const GET_TAG_VISITS_PROGRESS_CHANGED = 'shlink/tagVisits/GET_TAG_VISITS_PROGRESS_CHANGED';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use TagVisits interface instead */
export const TagVisitsType = PropTypes.shape({
visits: PropTypes.arrayOf(VisitType),
tag: PropTypes.string,
loading: PropTypes.bool,
loadingLarge: PropTypes.bool,
error: PropTypes.bool,
progress: PropTypes.number,
});
export interface TagVisits extends VisitsInfo {
tag: string;
}

View file

@ -1,19 +1,24 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow, ShallowWrapper } from 'enzyme';
import { identity } from 'ramda';
import createShortUrlVisits from '../../src/visits/ShortUrlVisits';
import { Mock } from 'ts-mockery';
import { History, Location } from 'history';
import { match } from 'react-router'; // eslint-disable-line @typescript-eslint/no-unused-vars
import createShortUrlVisits, { ShortUrlVisitsProps } from '../../src/visits/ShortUrlVisits';
import ShortUrlVisitsHeader from '../../src/visits/ShortUrlVisitsHeader';
import { ShortUrlVisits as ShortUrlVisitsState } from '../../src/visits/reducers/shortUrlVisits';
import { ShortUrlDetail } from '../../src/visits/reducers/shortUrlDetail';
describe('<ShortUrlVisits />', () => {
let wrapper;
let wrapper: ShallowWrapper;
const getShortUrlVisitsMock = jest.fn();
const match = {
const match = Mock.of<match<{ shortCode: string }>>({
params: { shortCode: 'abc123' },
};
const location = { search: '' };
const history = {
});
const location = Mock.of<Location>({ search: '' });
const history = Mock.of<History>({
goBack: jest.fn(),
};
});
const VisitsStats = jest.fn();
beforeEach(() => {
@ -21,15 +26,15 @@ describe('<ShortUrlVisits />', () => {
wrapper = shallow(
<ShortUrlVisits
{...Mock.all<ShortUrlVisitsProps>()}
getShortUrlDetail={identity}
getShortUrlVisits={getShortUrlVisitsMock}
match={match}
location={location}
history={history}
shortUrlVisits={{ loading: true, visits: [] }}
shortUrlDetail={{}}
shortUrlVisits={Mock.of<ShortUrlVisitsState>({ loading: true, visits: [] })}
shortUrlDetail={Mock.all<ShortUrlDetail>()}
cancelGetShortUrlVisits={identity}
matchMedia={() => ({ matches: false })}
/>,
);
});