Ensured tags deleted are not removed from list until modal has been hidden

This commit is contained in:
Alejandro Celaya 2022-11-07 22:19:44 +01:00
parent 0571a4a88f
commit dbb08a6ce0
4 changed files with 20 additions and 17 deletions

View file

@ -13,15 +13,14 @@ interface DeleteTagConfirmModalProps extends TagModalProps {
export const DeleteTagConfirmModal = ( export const DeleteTagConfirmModal = (
{ tag, toggle, isOpen, deleteTag, tagDelete, tagDeleted }: DeleteTagConfirmModalProps, { tag, toggle, isOpen, deleteTag, tagDelete, tagDeleted }: DeleteTagConfirmModalProps,
) => { ) => {
const { deleting, error, errorData } = tagDelete; const { deleting, error, deleted, errorData } = tagDelete;
const doDelete = async () => { const doDelete = async () => {
await deleteTag(tag); await deleteTag(tag);
tagDeleted(tag);
toggle(); toggle();
}; };
return ( return (
<Modal toggle={toggle} isOpen={isOpen} centered> <Modal toggle={toggle} isOpen={isOpen} centered onClosed={() => deleted && tagDeleted(tag)}>
<ModalHeader toggle={toggle} className="text-danger">Delete tag</ModalHeader> <ModalHeader toggle={toggle} className="text-danger">Delete tag</ModalHeader>
<ModalBody> <ModalBody>
Are you sure you want to delete tag <b>{tag}</b>? Are you sure you want to delete tag <b>{tag}</b>?

View file

@ -13,6 +13,7 @@ export const TAG_DELETED = 'shlink/deleteTag/TAG_DELETED';
export interface TagDeletion { export interface TagDeletion {
deleting: boolean; deleting: boolean;
deleted: boolean;
error: boolean; error: boolean;
errorData?: ProblemDetailsError; errorData?: ProblemDetailsError;
} }
@ -23,13 +24,14 @@ export interface DeleteTagAction extends Action<string> {
const initialState: TagDeletion = { const initialState: TagDeletion = {
deleting: false, deleting: false,
deleted: false,
error: false, error: false,
}; };
export default buildReducer<TagDeletion, ApiErrorAction>({ export default buildReducer<TagDeletion, ApiErrorAction>({
[DELETE_TAG_START]: () => ({ deleting: true, error: false }), [DELETE_TAG_START]: () => ({ deleting: true, deleted: false, error: false }),
[DELETE_TAG_ERROR]: (_, { errorData }) => ({ deleting: false, error: true, errorData }), [DELETE_TAG_ERROR]: (_, { errorData }) => ({ deleting: false, deleted: false, error: true, errorData }),
[DELETE_TAG]: () => ({ deleting: false, error: false }), [DELETE_TAG]: () => ({ deleting: false, deleted: true, error: false }),
}, initialState); }, initialState);
export const deleteTag = (buildShlinkApiClient: ShlinkApiClientBuilder) => (tag: string) => async ( export const deleteTag = (buildShlinkApiClient: ShlinkApiClientBuilder) => (tag: string) => async (

View file

@ -6,14 +6,14 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<DeleteTagConfirmModal />', () => { describe('<DeleteTagConfirmModal />', () => {
const tag = 'nodejs'; const tag = 'nodejs';
const deleteTag = jest.fn(); const deleteTag = jest.fn();
const tagDeleted = jest.fn(); const toggle = jest.fn();
const setUp = (tagDelete: TagDeletion) => renderWithEvents( const setUp = (tagDelete: TagDeletion) => renderWithEvents(
<DeleteTagConfirmModal <DeleteTagConfirmModal
tag={tag} tag={tag}
toggle={() => ''} toggle={toggle}
isOpen isOpen
deleteTag={deleteTag} deleteTag={deleteTag}
tagDeleted={tagDeleted} tagDeleted={jest.fn()}
tagDelete={tagDelete} tagDelete={tagDelete}
/>, />,
); );
@ -21,7 +21,7 @@ describe('<DeleteTagConfirmModal />', () => {
afterEach(jest.resetAllMocks); afterEach(jest.resetAllMocks);
it('asks confirmation for provided tag to be deleted', () => { it('asks confirmation for provided tag to be deleted', () => {
setUp({ error: false, deleting: false }); setUp({ error: false, deleted: false, deleting: false });
const delBtn = screen.getByRole('button', { name: 'Delete tag' }); const delBtn = screen.getByRole('button', { name: 'Delete tag' });
@ -33,12 +33,12 @@ describe('<DeleteTagConfirmModal />', () => {
}); });
it('shows error message when deletion failed', () => { it('shows error message when deletion failed', () => {
setUp({ error: true, deleting: false }); setUp({ error: true, deleted: false, deleting: false });
expect(screen.getByText('Something went wrong while deleting the tag :(')).toBeInTheDocument(); expect(screen.getByText('Something went wrong while deleting the tag :(')).toBeInTheDocument();
}); });
it('shows loading status while deleting', () => { it('shows loading status while deleting', () => {
setUp({ error: false, deleting: true }); setUp({ error: false, deleted: false, deleting: true });
const delBtn = screen.getByRole('button', { name: 'Deleting tag...' }); const delBtn = screen.getByRole('button', { name: 'Deleting tag...' });
@ -48,22 +48,21 @@ describe('<DeleteTagConfirmModal />', () => {
}); });
it('hides tag modal when btn is clicked', async () => { it('hides tag modal when btn is clicked', async () => {
const { user } = setUp({ error: false, deleting: false }); const { user } = setUp({ error: false, deleted: true, deleting: false });
await user.click(screen.getByRole('button', { name: 'Delete tag' })); await user.click(screen.getByRole('button', { name: 'Delete tag' }));
expect(deleteTag).toHaveBeenCalledTimes(1); expect(deleteTag).toHaveBeenCalledTimes(1);
expect(deleteTag).toHaveBeenCalledWith(tag); expect(deleteTag).toHaveBeenCalledWith(tag);
expect(tagDeleted).toHaveBeenCalledTimes(1); expect(toggle).toHaveBeenCalledTimes(1);
expect(tagDeleted).toHaveBeenCalledWith(tag);
}); });
it('does no further actions when modal is closed without deleting tag', async () => { it('does no further actions when modal is closed without deleting tag', async () => {
const { user } = setUp({ error: false, deleting: false }); const { user } = setUp({ error: false, deleted: true, deleting: false });
await user.click(screen.getByLabelText('Close')); await user.click(screen.getByLabelText('Close'));
expect(deleteTag).not.toHaveBeenCalled(); expect(deleteTag).not.toHaveBeenCalled();
expect(tagDeleted).not.toHaveBeenCalled(); expect(toggle).toHaveBeenCalled();
}); });
}); });

View file

@ -15,6 +15,7 @@ describe('tagDeleteReducer', () => {
it('returns loading on DELETE_TAG_START', () => { it('returns loading on DELETE_TAG_START', () => {
expect(reducer(undefined, { type: DELETE_TAG_START })).toEqual({ expect(reducer(undefined, { type: DELETE_TAG_START })).toEqual({
deleting: true, deleting: true,
deleted: false,
error: false, error: false,
}); });
}); });
@ -22,6 +23,7 @@ describe('tagDeleteReducer', () => {
it('returns error on DELETE_TAG_ERROR', () => { it('returns error on DELETE_TAG_ERROR', () => {
expect(reducer(undefined, { type: DELETE_TAG_ERROR })).toEqual({ expect(reducer(undefined, { type: DELETE_TAG_ERROR })).toEqual({
deleting: false, deleting: false,
deleted: false,
error: true, error: true,
}); });
}); });
@ -29,6 +31,7 @@ describe('tagDeleteReducer', () => {
it('returns tag names on DELETE_TAG', () => { it('returns tag names on DELETE_TAG', () => {
expect(reducer(undefined, { type: DELETE_TAG })).toEqual({ expect(reducer(undefined, { type: DELETE_TAG })).toEqual({
deleting: false, deleting: false,
deleted: true,
error: false, error: false,
}); });
}); });