From 2e963bdc8efca3b12cc01d725d562a9d210b357c Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 8 Jan 2022 10:51:34 +0100 Subject: [PATCH] Fixed short URLs list in overview page --- CHANGELOG.md | 1 + src/servers/Overview.tsx | 4 +- src/short-urls/reducers/shortUrlsList.ts | 9 ++-- .../short-urls/reducers/shortUrlsList.test.ts | 46 +++++++++++++++---- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 389c946f..ce8b0a99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed * [#555](https://github.com/shlinkio/shlink-web-client/issues/555) Fixed vertical alignment in welcome screen logo. +* [#554](https://github.com/shlinkio/shlink-web-client/issues/554) Fixed behavior in overview page, where items in the list of short URLs were stripped out when creating new ones, even if the amount of short URLs was still not yet big enough. ## [3.5.0] - 2022-01-01 diff --git a/src/servers/Overview.tsx b/src/servers/Overview.tsx index a68d3e17..64b9061d 100644 --- a/src/servers/Overview.tsx +++ b/src/servers/Overview.tsx @@ -1,7 +1,7 @@ import { FC, useEffect } from 'react'; import { Card, CardBody, CardHeader, CardText, CardTitle, Row } from 'reactstrap'; import { Link, useHistory } from 'react-router-dom'; -import { ShortUrlsList as ShortUrlsListState } from '../short-urls/reducers/shortUrlsList'; +import { ITEMS_IN_OVERVIEW_PAGE, ShortUrlsList as ShortUrlsListState } from '../short-urls/reducers/shortUrlsList'; import { prettify } from '../utils/helpers/numbers'; import { TagsList } from '../tags/reducers/tagsList'; import { ShortUrlsTableProps } from '../short-urls/ShortUrlsTable'; @@ -44,7 +44,7 @@ export const Overview = ( const history = useHistory(); useEffect(() => { - listShortUrls({ itemsPerPage: 5, orderBy: { field: 'dateCreated', dir: 'DESC' } }); + listShortUrls({ itemsPerPage: ITEMS_IN_OVERVIEW_PAGE, orderBy: { field: 'dateCreated', dir: 'DESC' } }); listTags(); loadVisitsOverview(); }, []); diff --git a/src/short-urls/reducers/shortUrlsList.ts b/src/short-urls/reducers/shortUrlsList.ts index 894975ac..c1f272da 100644 --- a/src/short-urls/reducers/shortUrlsList.ts +++ b/src/short-urls/reducers/shortUrlsList.ts @@ -1,4 +1,4 @@ -import { assoc, assocPath, init, last, pipe, reject } from 'ramda'; +import { assoc, assocPath, last, pipe, reject } from 'ramda'; import { Action, Dispatch } from 'redux'; import { shortUrlMatches } from '../helpers'; import { CREATE_VISITS, CreateVisitsAction } from '../../visits/reducers/visitCreation'; @@ -16,6 +16,8 @@ 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 */ +export const ITEMS_IN_OVERVIEW_PAGE = 5; + export interface ShortUrlsList { shortUrls?: ShlinkShortUrlsResponse; loading: boolean; @@ -75,10 +77,11 @@ export default buildReducer({ ), [CREATE_SHORT_URL]: pipe( // The only place where the list and the creation form coexist is the overview page. - // There we can assume we are displaying page 1, and therefore, we can safely prepend the new short URL and remove the last one. + // There we can assume we are displaying page 1, and therefore, we can safely prepend the new short URL. + // We can also remove the items above the amount that is displayed there. (state: ShortUrlsList, { result }: CreateShortUrlAction) => !state.shortUrls ? state : assocPath( [ 'shortUrls', 'data' ], - [ result, ...init(state.shortUrls.data) ], + [ result, ...state.shortUrls.data.slice(0, ITEMS_IN_OVERVIEW_PAGE - 1) ], state, ), (state: ShortUrlsList) => !state.shortUrls ? state : assocPath( diff --git a/test/short-urls/reducers/shortUrlsList.test.ts b/test/short-urls/reducers/shortUrlsList.test.ts index 031f6a23..8eac84c3 100644 --- a/test/short-urls/reducers/shortUrlsList.test.ts +++ b/test/short-urls/reducers/shortUrlsList.test.ts @@ -14,6 +14,8 @@ import { CREATE_SHORT_URL } from '../../../src/short-urls/reducers/shortUrlCreat import { SHORT_URL_EDITED } from '../../../src/short-urls/reducers/shortUrlEdition'; describe('shortUrlsListReducer', () => { + const shortCode = 'abc123'; + describe('reducer', () => { it('returns loading on LIST_SHORT_URLS_START', () => expect(reducer(undefined, { type: LIST_SHORT_URLS_START } as any)).toEqual({ @@ -35,7 +37,6 @@ describe('shortUrlsListReducer', () => { })); it('removes matching URL and reduces total on SHORT_URL_DELETED', () => { - const shortCode = 'abc123'; const state = { shortUrls: Mock.of({ data: [ @@ -72,7 +73,6 @@ describe('shortUrlsListReducer', () => { [[{}], 10 ], [[], 10 ], ])('updates visits count on CREATE_VISITS', (createdVisits, expectedCount) => { - const shortCode = 'abc123'; const state = { shortUrls: Mock.of({ data: [ @@ -98,16 +98,42 @@ describe('shortUrlsListReducer', () => { }); }); - it('prepends new short URL and increases total on CREATE_SHORT_URL', () => { + it.each([ + [ + [ + Mock.of({ shortCode }), + Mock.of({ shortCode, domain: 'example.com' }), + Mock.of({ shortCode: 'foo' }), + ], + [{ shortCode: 'newOne' }, { shortCode }, { shortCode, domain: 'example.com' }, { shortCode: 'foo' }], + ], + [ + [ + Mock.of({ shortCode }), + Mock.of({ shortCode: 'code' }), + Mock.of({ shortCode: 'foo' }), + Mock.of({ shortCode: 'bar' }), + Mock.of({ shortCode: 'baz' }), + ], + [{ shortCode: 'newOne' }, { shortCode }, { shortCode: 'code' }, { shortCode: 'foo' }, { shortCode: 'bar' }], + ], + [ + [ + Mock.of({ shortCode }), + Mock.of({ shortCode: 'code' }), + Mock.of({ shortCode: 'foo' }), + Mock.of({ shortCode: 'bar' }), + Mock.of({ shortCode: 'baz1' }), + Mock.of({ shortCode: 'baz2' }), + Mock.of({ shortCode: 'baz3' }), + ], + [{ shortCode: 'newOne' }, { shortCode }, { shortCode: 'code' }, { shortCode: 'foo' }, { shortCode: 'bar' }], + ], + ])('prepends new short URL and increases total on CREATE_SHORT_URL', (data, expectedData) => { const newShortUrl = Mock.of({ shortCode: 'newOne' }); - const shortCode = 'abc123'; const state = { shortUrls: Mock.of({ - data: [ - Mock.of({ shortCode }), - Mock.of({ shortCode, domain: 'example.com' }), - Mock.of({ shortCode: 'foo' }), - ], + data, pagination: Mock.of({ totalItems: 15, }), @@ -118,7 +144,7 @@ describe('shortUrlsListReducer', () => { expect(reducer(state, { type: CREATE_SHORT_URL, result: newShortUrl } as any)).toEqual({ shortUrls: { - data: [{ shortCode: 'newOne' }, { shortCode }, { shortCode, domain: 'example.com' }], + data: expectedData, pagination: { totalItems: 16 }, }, loading: false,