From d21758c410b35a987ee1e30df04d89d29d418f03 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 22 Nov 2022 19:39:07 +0100 Subject: [PATCH] Fixed DeleteShortUrlModal being removed from the DOM before CSS transition finished --- .../helpers/DeleteShortUrlModal.tsx | 21 ++++++++++++------- src/short-urls/reducers/shortUrlDeletion.ts | 6 ++++-- src/short-urls/reducers/shortUrlsList.ts | 5 ++--- src/short-urls/services/provideServices.ts | 9 +++++--- .../helpers/DeleteShortUrlModal.test.tsx | 11 ++++++---- .../short-urls/reducers/shortUrlsList.test.ts | 7 +++---- 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/short-urls/helpers/DeleteShortUrlModal.tsx b/src/short-urls/helpers/DeleteShortUrlModal.tsx index d240279f..37582b95 100644 --- a/src/short-urls/helpers/DeleteShortUrlModal.tsx +++ b/src/short-urls/helpers/DeleteShortUrlModal.tsx @@ -10,23 +10,30 @@ import { ShlinkApiError } from '../../api/ShlinkApiError'; interface DeleteShortUrlModalConnectProps extends ShortUrlModalProps { shortUrlDeletion: ShortUrlDeletion; - deleteShortUrl: (shortUrl: ShortUrlIdentifier) => void; + deleteShortUrl: (shortUrl: ShortUrlIdentifier) => Promise; + shortUrlDeleted: (shortUrl: ShortUrlIdentifier) => void; resetDeleteShortUrl: () => void; } -export const DeleteShortUrlModal = ( - { shortUrl, toggle, isOpen, shortUrlDeletion, resetDeleteShortUrl, deleteShortUrl }: DeleteShortUrlModalConnectProps, -) => { +export const DeleteShortUrlModal = ({ + shortUrl, + toggle, + isOpen, + shortUrlDeletion, + resetDeleteShortUrl, + deleteShortUrl, + shortUrlDeleted, +}: DeleteShortUrlModalConnectProps) => { const [inputValue, setInputValue] = useState(''); useEffect(() => resetDeleteShortUrl, []); - const { loading, error, errorData } = shortUrlDeletion; + const { loading, error, deleted, errorData } = shortUrlDeletion; const close = pipe(resetDeleteShortUrl, toggle); - const handleDeleteUrl = handleEventPreventingDefault(() => deleteShortUrl(shortUrl)); + const handleDeleteUrl = handleEventPreventingDefault(() => deleteShortUrl(shortUrl).then(toggle)); return ( - + deleted && shortUrlDeleted(shortUrl)}>
Delete short URL diff --git a/src/short-urls/reducers/shortUrlDeletion.ts b/src/short-urls/reducers/shortUrlDeletion.ts index 67d86499..294e1986 100644 --- a/src/short-urls/reducers/shortUrlDeletion.ts +++ b/src/short-urls/reducers/shortUrlDeletion.ts @@ -1,9 +1,9 @@ -import { createSlice } from '@reduxjs/toolkit'; +import { createAction, createSlice } from '@reduxjs/toolkit'; import { createAsyncThunk } from '../../utils/helpers/redux'; import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { parseApiError } from '../../api/utils'; import { ProblemDetailsError } from '../../api/types/errors'; -import { ShortUrlIdentifier } from '../data'; +import { ShortUrl, ShortUrlIdentifier } from '../data'; const REDUCER_PREFIX = 'shlink/shortUrlDeletion'; @@ -31,6 +31,8 @@ export const deleteShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => }, ); +export const shortUrlDeleted = createAction(`${REDUCER_PREFIX}/shortUrlDeleted`); + export const shortUrlDeletionReducerCreator = (deleteShortUrlThunk: ReturnType) => { const { actions, reducer } = createSlice({ name: REDUCER_PREFIX, diff --git a/src/short-urls/reducers/shortUrlsList.ts b/src/short-urls/reducers/shortUrlsList.ts index c6885353..66ba341d 100644 --- a/src/short-urls/reducers/shortUrlsList.ts +++ b/src/short-urls/reducers/shortUrlsList.ts @@ -5,7 +5,7 @@ import { createNewVisits } from '../../visits/reducers/visitCreation'; import { createAsyncThunk } from '../../utils/helpers/redux'; import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { ShlinkShortUrlsListParams, ShlinkShortUrlsResponse } from '../../api/types'; -import { deleteShortUrl } from './shortUrlDeletion'; +import { shortUrlDeleted } from './shortUrlDeletion'; import { createShortUrl } from './shortUrlCreation'; import { editShortUrl } from './shortUrlEdition'; import { ShortUrl } from '../data'; @@ -36,7 +36,6 @@ export const shortUrlsListReducerCreator = ( listShortUrlsThunk: ReturnType, editShortUrlThunk: ReturnType, createShortUrlThunk: ReturnType, - deleteShortUrlThunk: ReturnType, ) => createSlice({ name: REDUCER_PREFIX, initialState, @@ -81,7 +80,7 @@ export const shortUrlsListReducerCreator = ( ); builder.addCase( - deleteShortUrlThunk.fulfilled, + shortUrlDeleted, pipe( (state, { payload }) => (!state.shortUrls ? state : assocPath( ['shortUrls', 'data'], diff --git a/src/short-urls/services/provideServices.ts b/src/short-urls/services/provideServices.ts index 4ddb4ea7..eeb10ce9 100644 --- a/src/short-urls/services/provideServices.ts +++ b/src/short-urls/services/provideServices.ts @@ -9,7 +9,7 @@ import { DeleteShortUrlModal } from '../helpers/DeleteShortUrlModal'; import { CreateShortUrlResult } from '../helpers/CreateShortUrlResult'; import { listShortUrls, shortUrlsListReducerCreator } from '../reducers/shortUrlsList'; import { shortUrlCreationReducerCreator, createShortUrl } from '../reducers/shortUrlCreation'; -import { shortUrlDeletionReducerCreator, deleteShortUrl } from '../reducers/shortUrlDeletion'; +import { shortUrlDeletionReducerCreator, deleteShortUrl, shortUrlDeleted } from '../reducers/shortUrlDeletion'; import { editShortUrl, shortUrlEditionReducerCreator } from '../reducers/shortUrlEdition'; import { shortUrlDetailReducerCreator } from '../reducers/shortUrlDetail'; import { ConnectDecorator } from '../../container/types'; @@ -46,7 +46,10 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { )); bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal); - bottle.decorator('DeleteShortUrlModal', connect(['shortUrlDeletion'], ['deleteShortUrl', 'resetDeleteShortUrl'])); + bottle.decorator('DeleteShortUrlModal', connect( + ['shortUrlDeletion'], + ['deleteShortUrl', 'shortUrlDeleted', 'resetDeleteShortUrl'], + )); bottle.serviceFactory('QrCodeModal', QrCodeModal, 'ImageDownloader'); bottle.decorator('QrCodeModal', connect(['selectedServer'])); @@ -63,7 +66,6 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { 'listShortUrls', 'editShortUrl', 'createShortUrl', - 'deleteShortUrl', ); bottle.serviceFactory('shortUrlsListReducer', prop('reducer'), 'shortUrlsListReducerCreator'); @@ -87,6 +89,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('deleteShortUrl', deleteShortUrl, 'buildShlinkApiClient'); bottle.serviceFactory('resetDeleteShortUrl', prop('resetDeleteShortUrl'), 'shortUrlDeletionReducerCreator'); + bottle.serviceFactory('shortUrlDeleted', () => shortUrlDeleted); bottle.serviceFactory('getShortUrlDetail', prop('getShortUrlDetail'), 'shortUrlDetailReducerCreator'); diff --git a/test/short-urls/helpers/DeleteShortUrlModal.test.tsx b/test/short-urls/helpers/DeleteShortUrlModal.test.tsx index 282c4738..d4145296 100644 --- a/test/short-urls/helpers/DeleteShortUrlModal.test.tsx +++ b/test/short-urls/helpers/DeleteShortUrlModal.test.tsx @@ -1,4 +1,4 @@ -import { screen } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import { Mock } from 'ts-mockery'; import { DeleteShortUrlModal } from '../../../src/short-urls/helpers/DeleteShortUrlModal'; import { ShortUrl } from '../../../src/short-urls/data'; @@ -12,15 +12,17 @@ describe('', () => { shortCode: 'abc123', longUrl: 'https://long-domain.com/foo/bar', }); - const deleteShortUrl = jest.fn(); + const deleteShortUrl = jest.fn().mockResolvedValue(undefined); + const toggle = jest.fn(); const setUp = (shortUrlDeletion: Partial) => renderWithEvents( (shortUrlDeletion)} deleteShortUrl={deleteShortUrl} - toggle={() => {}} - resetDeleteShortUrl={() => {}} + shortUrlDeleted={jest.fn()} + toggle={toggle} + resetDeleteShortUrl={jest.fn()} />, ); @@ -81,5 +83,6 @@ describe('', () => { await user.type(screen.getByPlaceholderText(/^Insert the short code/), shortCode); await user.click(screen.getByRole('button', { name: 'Delete' })); expect(deleteShortUrl).toHaveBeenCalledTimes(1); + await waitFor(() => expect(toggle).toHaveBeenCalledTimes(1)); }); }); diff --git a/test/short-urls/reducers/shortUrlsList.test.ts b/test/short-urls/reducers/shortUrlsList.test.ts index cc9e748d..8e639603 100644 --- a/test/short-urls/reducers/shortUrlsList.test.ts +++ b/test/short-urls/reducers/shortUrlsList.test.ts @@ -3,7 +3,7 @@ import { listShortUrls as listShortUrlsCreator, shortUrlsListReducerCreator, } from '../../../src/short-urls/reducers/shortUrlsList'; -import { deleteShortUrl as deleteShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlDeletion'; +import { shortUrlDeleted } from '../../../src/short-urls/reducers/shortUrlDeletion'; import { ShlinkPaginator, ShlinkShortUrlsResponse } from '../../../src/api/types'; import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation'; import { editShortUrl as editShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlEdition'; @@ -18,8 +18,7 @@ describe('shortUrlsListReducer', () => { const listShortUrls = listShortUrlsCreator(buildShlinkApiClient); const editShortUrl = editShortUrlCreator(buildShlinkApiClient); const createShortUrl = createShortUrlCreator(buildShlinkApiClient); - const deleteShortUrl = deleteShortUrlCreator(buildShlinkApiClient); - const { reducer } = shortUrlsListReducerCreator(listShortUrls, editShortUrl, createShortUrl, deleteShortUrl); + const { reducer } = shortUrlsListReducerCreator(listShortUrls, editShortUrl, createShortUrl); afterEach(jest.clearAllMocks); @@ -59,7 +58,7 @@ describe('shortUrlsListReducer', () => { error: false, }; - expect(reducer(state, { type: deleteShortUrl.fulfilled.toString(), payload: { shortCode } })).toEqual({ + expect(reducer(state, { type: shortUrlDeleted.toString(), payload: { shortCode } })).toEqual({ shortUrls: { data: [{ shortCode, domain: 'example.com' }, { shortCode: 'foo' }], pagination: { totalItems: 9 },