mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Ensured tags list is not updated until the edit modal is closed
This commit is contained in:
parent
2183b09ffe
commit
5ecc791b38
4 changed files with 19 additions and 21 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import { pipe } from 'ramda';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button, Input, Modal, ModalBody, ModalFooter, ModalHeader, Popover, InputGroup } from 'reactstrap';
|
import { Button, Input, Modal, ModalBody, ModalFooter, ModalHeader, Popover, InputGroup } from 'reactstrap';
|
||||||
import { HexColorPicker } from 'react-colorful';
|
import { HexColorPicker } from 'react-colorful';
|
||||||
|
@ -24,16 +25,16 @@ export const EditTagModal = ({ getColorForKey }: ColorGenerator) => (
|
||||||
const [newTagName, setNewTagName] = useState(tag);
|
const [newTagName, setNewTagName] = useState(tag);
|
||||||
const [color, setColor] = useState(getColorForKey(tag));
|
const [color, setColor] = useState(getColorForKey(tag));
|
||||||
const [showColorPicker, toggleColorPicker, , hideColorPicker] = useToggle();
|
const [showColorPicker, toggleColorPicker, , hideColorPicker] = useToggle();
|
||||||
const { editing, error, errorData } = tagEdit;
|
const { editing, error, edited, errorData } = tagEdit;
|
||||||
const saveTag = handleEventPreventingDefault(
|
const saveTag = handleEventPreventingDefault(
|
||||||
async () => editTag(tag, newTagName, color)
|
async () => editTag(tag, newTagName, color)
|
||||||
.then(() => tagEdited(tag, newTagName, color))
|
|
||||||
.then(toggle)
|
.then(toggle)
|
||||||
.catch(() => {}),
|
.catch(() => {}),
|
||||||
);
|
);
|
||||||
|
const onClosed = pipe(hideColorPicker, () => edited && tagEdited(tag, newTagName, color));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} toggle={toggle} centered onClosed={hideColorPicker}>
|
<Modal isOpen={isOpen} toggle={toggle} centered onClosed={onClosed}>
|
||||||
<form name="editTag" onSubmit={saveTag}>
|
<form name="editTag" onSubmit={saveTag}>
|
||||||
<ModalHeader toggle={toggle}>Edit tag</ModalHeader>
|
<ModalHeader toggle={toggle}>Edit tag</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
|
|
@ -11,13 +11,13 @@ import { ProblemDetailsError } from '../../api/types/errors';
|
||||||
export const EDIT_TAG_START = 'shlink/editTag/EDIT_TAG_START';
|
export const EDIT_TAG_START = 'shlink/editTag/EDIT_TAG_START';
|
||||||
export const EDIT_TAG_ERROR = 'shlink/editTag/EDIT_TAG_ERROR';
|
export const EDIT_TAG_ERROR = 'shlink/editTag/EDIT_TAG_ERROR';
|
||||||
export const EDIT_TAG = 'shlink/editTag/EDIT_TAG';
|
export const EDIT_TAG = 'shlink/editTag/EDIT_TAG';
|
||||||
|
|
||||||
export const TAG_EDITED = 'shlink/editTag/TAG_EDITED';
|
export const TAG_EDITED = 'shlink/editTag/TAG_EDITED';
|
||||||
|
|
||||||
export interface TagEdition {
|
export interface TagEdition {
|
||||||
oldName: string;
|
oldName?: string;
|
||||||
newName: string;
|
newName?: string;
|
||||||
editing: boolean;
|
editing: boolean;
|
||||||
|
edited: boolean;
|
||||||
error: boolean;
|
error: boolean;
|
||||||
errorData?: ProblemDetailsError;
|
errorData?: ProblemDetailsError;
|
||||||
}
|
}
|
||||||
|
@ -29,18 +29,18 @@ export interface EditTagAction extends Action<string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: TagEdition = {
|
const initialState: TagEdition = {
|
||||||
oldName: '',
|
|
||||||
newName: '',
|
|
||||||
editing: false,
|
editing: false,
|
||||||
|
edited: false,
|
||||||
error: false,
|
error: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default buildReducer<TagEdition, EditTagAction & ApiErrorAction>({
|
export default buildReducer<TagEdition, EditTagAction & ApiErrorAction>({
|
||||||
[EDIT_TAG_START]: (state) => ({ ...state, editing: true, error: false }),
|
[EDIT_TAG_START]: () => ({ editing: true, edited: false, error: false }),
|
||||||
[EDIT_TAG_ERROR]: (state, { errorData }) => ({ ...state, editing: false, error: true, errorData }),
|
[EDIT_TAG_ERROR]: (_, { errorData }) => ({ editing: false, edited: false, error: true, errorData }),
|
||||||
[EDIT_TAG]: (_, action) => ({
|
[EDIT_TAG]: (_, action) => ({
|
||||||
...pick(['oldName', 'newName'], action),
|
...pick(['oldName', 'newName'], action),
|
||||||
editing: false,
|
editing: false,
|
||||||
|
edited: true,
|
||||||
error: false,
|
error: false,
|
||||||
}),
|
}),
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
@ -56,7 +56,7 @@ export const editTag = (buildShlinkApiClient: ShlinkApiClientBuilder, colorGener
|
||||||
try {
|
try {
|
||||||
await shlinkEditTag(oldName, newName);
|
await shlinkEditTag(oldName, newName);
|
||||||
colorGenerator.setColorForKey(newName, color);
|
colorGenerator.setColorForKey(newName, color);
|
||||||
dispatch({ type: EDIT_TAG, oldName, newName });
|
dispatch({ type: EDIT_TAG, oldName, newName, color });
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
dispatch<ApiErrorAction>({ type: EDIT_TAG_ERROR, errorData: parseApiError(e) });
|
dispatch<ApiErrorAction>({ type: EDIT_TAG_ERROR, errorData: parseApiError(e) });
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,11 @@ import { ProblemDetailsError } from '../../../src/api/types/errors';
|
||||||
describe('<EditTagModal />', () => {
|
describe('<EditTagModal />', () => {
|
||||||
const EditTagModal = createEditTagModal(Mock.of<ColorGenerator>({ getColorForKey: jest.fn(() => 'green') }));
|
const EditTagModal = createEditTagModal(Mock.of<ColorGenerator>({ getColorForKey: jest.fn(() => 'green') }));
|
||||||
const editTag = jest.fn().mockReturnValue(Promise.resolve());
|
const editTag = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
const tagEdited = jest.fn().mockReturnValue(Promise.resolve());
|
|
||||||
const toggle = jest.fn();
|
const toggle = jest.fn();
|
||||||
const setUp = (tagEdit: Partial<TagEdition> = {}) => {
|
const setUp = (tagEdit: Partial<TagEdition> = {}) => {
|
||||||
const edition = Mock.of<TagEdition>(tagEdit);
|
const edition = Mock.of<TagEdition>(tagEdit);
|
||||||
return renderWithEvents(
|
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(toggle).toHaveBeenCalledTimes(2);
|
||||||
expect(editTag).not.toHaveBeenCalled();
|
expect(editTag).not.toHaveBeenCalled();
|
||||||
expect(tagEdited).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
@ -63,12 +61,12 @@ describe('<EditTagModal />', () => {
|
||||||
const { user } = setUp();
|
const { user } = setUp();
|
||||||
|
|
||||||
expect(editTag).not.toHaveBeenCalled();
|
expect(editTag).not.toHaveBeenCalled();
|
||||||
expect(tagEdited).not.toHaveBeenCalled();
|
expect(toggle).not.toHaveBeenCalled();
|
||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: 'Save' }));
|
await user.click(screen.getByRole('button', { name: 'Save' }));
|
||||||
|
|
||||||
expect(editTag).toHaveBeenCalled();
|
expect(editTag).toHaveBeenCalled();
|
||||||
expect(tagEdited).toHaveBeenCalled();
|
expect(toggle).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes color when changing on color picker', async () => {
|
it('changes color when changing on color picker', async () => {
|
||||||
|
|
|
@ -21,24 +21,23 @@ describe('tagEditReducer', () => {
|
||||||
it('returns loading on EDIT_TAG_START', () => {
|
it('returns loading on EDIT_TAG_START', () => {
|
||||||
expect(reducer(undefined, Mock.of<EditTagAction>({ type: EDIT_TAG_START }))).toEqual({
|
expect(reducer(undefined, Mock.of<EditTagAction>({ type: EDIT_TAG_START }))).toEqual({
|
||||||
editing: true,
|
editing: true,
|
||||||
|
edited: false,
|
||||||
error: false,
|
error: false,
|
||||||
oldName: '',
|
|
||||||
newName: '',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error on EDIT_TAG_ERROR', () => {
|
it('returns error on EDIT_TAG_ERROR', () => {
|
||||||
expect(reducer(undefined, Mock.of<EditTagAction>({ type: EDIT_TAG_ERROR }))).toEqual({
|
expect(reducer(undefined, Mock.of<EditTagAction>({ type: EDIT_TAG_ERROR }))).toEqual({
|
||||||
editing: false,
|
editing: false,
|
||||||
|
edited: false,
|
||||||
error: true,
|
error: true,
|
||||||
oldName: '',
|
|
||||||
newName: '',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns tag names on EDIT_TAG', () => {
|
it('returns tag names on EDIT_TAG', () => {
|
||||||
expect(reducer(undefined, { type: EDIT_TAG, oldName, newName, color })).toEqual({
|
expect(reducer(undefined, { type: EDIT_TAG, oldName, newName, color })).toEqual({
|
||||||
editing: false,
|
editing: false,
|
||||||
|
edited: true,
|
||||||
error: false,
|
error: false,
|
||||||
oldName: 'foo',
|
oldName: 'foo',
|
||||||
newName: 'bar',
|
newName: 'bar',
|
||||||
|
@ -82,7 +81,7 @@ describe('tagEditReducer', () => {
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_TAG_START });
|
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 () => {
|
it('throws on error', async () => {
|
||||||
|
|
Loading…
Reference in a new issue