From 5ecc791b38ecbf77a5d7c388e0c3e8f5e62d5eb6 Mon Sep 17 00:00:00 2001
From: Alejandro Celaya <alejandrocelaya@gmail.com>
Date: Mon, 7 Nov 2022 21:32:19 +0100
Subject: [PATCH] Ensured tags list is not updated until the edit modal is
 closed

---
 src/tags/helpers/EditTagModal.tsx       |  7 ++++---
 src/tags/reducers/tagEdit.ts            | 16 ++++++++--------
 test/tags/helpers/EditTagModal.test.tsx |  8 +++-----
 test/tags/reducers/tagEdit.test.ts      |  9 ++++-----
 4 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/src/tags/helpers/EditTagModal.tsx b/src/tags/helpers/EditTagModal.tsx
index 4b6af3d7..55b5ccb6 100644
--- a/src/tags/helpers/EditTagModal.tsx
+++ b/src/tags/helpers/EditTagModal.tsx
@@ -1,3 +1,4 @@
+import { pipe } from 'ramda';
 import { useState } from 'react';
 import { Button, Input, Modal, ModalBody, ModalFooter, ModalHeader, Popover, InputGroup } from 'reactstrap';
 import { HexColorPicker } from 'react-colorful';
@@ -24,16 +25,16 @@ export const EditTagModal = ({ getColorForKey }: ColorGenerator) => (
   const [newTagName, setNewTagName] = useState(tag);
   const [color, setColor] = useState(getColorForKey(tag));
   const [showColorPicker, toggleColorPicker, , hideColorPicker] = useToggle();
-  const { editing, error, errorData } = tagEdit;
+  const { editing, error, edited, errorData } = tagEdit;
   const saveTag = handleEventPreventingDefault(
     async () => editTag(tag, newTagName, color)
-      .then(() => tagEdited(tag, newTagName, color))
       .then(toggle)
       .catch(() => {}),
   );
+  const onClosed = pipe(hideColorPicker, () => edited && tagEdited(tag, newTagName, color));
 
   return (
-    <Modal isOpen={isOpen} toggle={toggle} centered onClosed={hideColorPicker}>
+    <Modal isOpen={isOpen} toggle={toggle} centered onClosed={onClosed}>
       <form name="editTag" onSubmit={saveTag}>
         <ModalHeader toggle={toggle}>Edit tag</ModalHeader>
         <ModalBody>
diff --git a/src/tags/reducers/tagEdit.ts b/src/tags/reducers/tagEdit.ts
index b3582f62..19a8599f 100644
--- a/src/tags/reducers/tagEdit.ts
+++ b/src/tags/reducers/tagEdit.ts
@@ -11,13 +11,13 @@ import { ProblemDetailsError } from '../../api/types/errors';
 export const EDIT_TAG_START = 'shlink/editTag/EDIT_TAG_START';
 export const EDIT_TAG_ERROR = 'shlink/editTag/EDIT_TAG_ERROR';
 export const EDIT_TAG = 'shlink/editTag/EDIT_TAG';
-
 export const TAG_EDITED = 'shlink/editTag/TAG_EDITED';
 
 export interface TagEdition {
-  oldName: string;
-  newName: string;
+  oldName?: string;
+  newName?: string;
   editing: boolean;
+  edited: boolean;
   error: boolean;
   errorData?: ProblemDetailsError;
 }
@@ -29,18 +29,18 @@ export interface EditTagAction extends Action<string> {
 }
 
 const initialState: TagEdition = {
-  oldName: '',
-  newName: '',
   editing: false,
+  edited: false,
   error: false,
 };
 
 export default buildReducer<TagEdition, EditTagAction & ApiErrorAction>({
-  [EDIT_TAG_START]: (state) => ({ ...state, editing: true, error: false }),
-  [EDIT_TAG_ERROR]: (state, { errorData }) => ({ ...state, editing: false, error: true, errorData }),
+  [EDIT_TAG_START]: () => ({ editing: true, edited: false, error: false }),
+  [EDIT_TAG_ERROR]: (_, { errorData }) => ({ editing: false, edited: false, error: true, errorData }),
   [EDIT_TAG]: (_, action) => ({
     ...pick(['oldName', 'newName'], action),
     editing: false,
+    edited: true,
     error: false,
   }),
 }, initialState);
@@ -56,7 +56,7 @@ export const editTag = (buildShlinkApiClient: ShlinkApiClientBuilder, colorGener
   try {
     await shlinkEditTag(oldName, newName);
     colorGenerator.setColorForKey(newName, color);
-    dispatch({ type: EDIT_TAG, oldName, newName });
+    dispatch({ type: EDIT_TAG, oldName, newName, color });
   } catch (e: any) {
     dispatch<ApiErrorAction>({ type: EDIT_TAG_ERROR, errorData: parseApiError(e) });
 
diff --git a/test/tags/helpers/EditTagModal.test.tsx b/test/tags/helpers/EditTagModal.test.tsx
index 46820437..b4bc1dbf 100644
--- a/test/tags/helpers/EditTagModal.test.tsx
+++ b/test/tags/helpers/EditTagModal.test.tsx
@@ -9,12 +9,11 @@ import { ProblemDetailsError } from '../../../src/api/types/errors';
 describe('<EditTagModal />', () => {
   const EditTagModal = createEditTagModal(Mock.of<ColorGenerator>({ getColorForKey: jest.fn(() => 'green') }));
   const editTag = jest.fn().mockReturnValue(Promise.resolve());
-  const tagEdited = jest.fn().mockReturnValue(Promise.resolve());
   const toggle = jest.fn();
   const setUp = (tagEdit: Partial<TagEdition> = {}) => {
     const edition = Mock.of<TagEdition>(tagEdit);
     return renderWithEvents(
-      <EditTagModal isOpen tag="foo" tagEdit={edition} editTag={editTag} tagEdited={tagEdited} toggle={toggle} />,
+      <EditTagModal isOpen tag="foo" tagEdit={edition} editTag={editTag} tagEdited={jest.fn()} toggle={toggle} />,
     );
   };
 
@@ -30,7 +29,6 @@ describe('<EditTagModal />', () => {
 
     expect(toggle).toHaveBeenCalledTimes(2);
     expect(editTag).not.toHaveBeenCalled();
-    expect(tagEdited).not.toHaveBeenCalled();
   });
 
   it.each([
@@ -63,12 +61,12 @@ describe('<EditTagModal />', () => {
     const { user } = setUp();
 
     expect(editTag).not.toHaveBeenCalled();
-    expect(tagEdited).not.toHaveBeenCalled();
+    expect(toggle).not.toHaveBeenCalled();
 
     await user.click(screen.getByRole('button', { name: 'Save' }));
 
     expect(editTag).toHaveBeenCalled();
-    expect(tagEdited).toHaveBeenCalled();
+    expect(toggle).toHaveBeenCalled();
   });
 
   it('changes color when changing on color picker', async () => {
diff --git a/test/tags/reducers/tagEdit.test.ts b/test/tags/reducers/tagEdit.test.ts
index d000b343..994b5b3a 100644
--- a/test/tags/reducers/tagEdit.test.ts
+++ b/test/tags/reducers/tagEdit.test.ts
@@ -21,24 +21,23 @@ describe('tagEditReducer', () => {
     it('returns loading on EDIT_TAG_START', () => {
       expect(reducer(undefined, Mock.of<EditTagAction>({ type: EDIT_TAG_START }))).toEqual({
         editing: true,
+        edited: false,
         error: false,
-        oldName: '',
-        newName: '',
       });
     });
 
     it('returns error on EDIT_TAG_ERROR', () => {
       expect(reducer(undefined, Mock.of<EditTagAction>({ type: EDIT_TAG_ERROR }))).toEqual({
         editing: false,
+        edited: false,
         error: true,
-        oldName: '',
-        newName: '',
       });
     });
 
     it('returns tag names on EDIT_TAG', () => {
       expect(reducer(undefined, { type: EDIT_TAG, oldName, newName, color })).toEqual({
         editing: false,
+        edited: true,
         error: false,
         oldName: 'foo',
         newName: 'bar',
@@ -82,7 +81,7 @@ describe('tagEditReducer', () => {
 
       expect(dispatch).toHaveBeenCalledTimes(2);
       expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_TAG_START });
-      expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_TAG, oldName, newName });
+      expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_TAG, oldName, newName, color });
     });
 
     it('throws on error', async () => {