mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Simplified EditTagsModal component and shortUrlTags reducer
This commit is contained in:
parent
93f33b6218
commit
30117bd121
5 changed files with 16 additions and 73 deletions
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
|
import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ExternalLink } from 'react-external-link';
|
import { ExternalLink } from 'react-external-link';
|
||||||
|
import { pipe } from 'ramda';
|
||||||
import { shortUrlTagsType } from '../reducers/shortUrlTags';
|
import { shortUrlTagsType } from '../reducers/shortUrlTags';
|
||||||
import { shortUrlType } from '../reducers/shortUrlsList';
|
import { shortUrlType } from '../reducers/shortUrlsList';
|
||||||
|
|
||||||
|
@ -12,7 +13,6 @@ const EditTagsModal = (TagsSelector) => class EditTagsModal extends React.Compon
|
||||||
shortUrl: shortUrlType.isRequired,
|
shortUrl: shortUrlType.isRequired,
|
||||||
shortUrlTags: shortUrlTagsType,
|
shortUrlTags: shortUrlTagsType,
|
||||||
editShortUrlTags: PropTypes.func,
|
editShortUrlTags: PropTypes.func,
|
||||||
shortUrlTagsEdited: PropTypes.func,
|
|
||||||
resetShortUrlsTags: PropTypes.func,
|
resetShortUrlsTags: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,28 +20,14 @@ const EditTagsModal = (TagsSelector) => class EditTagsModal extends React.Compon
|
||||||
const { editShortUrlTags, shortUrl, toggle } = this.props;
|
const { editShortUrlTags, shortUrl, toggle } = this.props;
|
||||||
|
|
||||||
editShortUrlTags(shortUrl.shortCode, this.state.tags)
|
editShortUrlTags(shortUrl.shortCode, this.state.tags)
|
||||||
.then(() => {
|
.then(toggle)
|
||||||
this.tagsSaved = true;
|
|
||||||
toggle();
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
};
|
};
|
||||||
refreshShortUrls = () => {
|
|
||||||
if (!this.tagsSaved) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { shortUrlTagsEdited, shortUrl, shortUrlTags } = this.props;
|
|
||||||
const { tags } = shortUrlTags;
|
|
||||||
|
|
||||||
shortUrlTagsEdited(shortUrl.shortCode, tags);
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { resetShortUrlsTags } = this.props;
|
const { resetShortUrlsTags } = this.props;
|
||||||
|
|
||||||
resetShortUrlsTags();
|
resetShortUrlsTags();
|
||||||
this.tagsSaved = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -50,12 +36,13 @@ const EditTagsModal = (TagsSelector) => class EditTagsModal extends React.Compon
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isOpen, toggle, shortUrl, shortUrlTags } = this.props;
|
const { isOpen, toggle, shortUrl, shortUrlTags, resetShortUrlsTags } = this.props;
|
||||||
const url = shortUrl && (shortUrl.shortUrl || '');
|
const url = shortUrl && (shortUrl.shortUrl || '');
|
||||||
|
const close = pipe(resetShortUrlsTags, toggle);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} toggle={toggle} centered onClosed={() => this.refreshShortUrls()}>
|
<Modal isOpen={isOpen} toggle={close} centered>
|
||||||
<ModalHeader toggle={toggle}>
|
<ModalHeader toggle={close}>
|
||||||
Edit tags for <ExternalLink href={url} />
|
Edit tags for <ExternalLink href={url} />
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
@ -67,7 +54,7 @@ const EditTagsModal = (TagsSelector) => class EditTagsModal extends React.Compon
|
||||||
)}
|
)}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<button className="btn btn-link" onClick={toggle}>Cancel</button>
|
<button className="btn btn-link" onClick={close}>Cancel</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
@ -4,9 +4,8 @@ import PropTypes from 'prop-types';
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START';
|
export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START';
|
||||||
export const EDIT_SHORT_URL_TAGS_ERROR = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_ERROR';
|
export const EDIT_SHORT_URL_TAGS_ERROR = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_ERROR';
|
||||||
export const EDIT_SHORT_URL_TAGS = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS';
|
|
||||||
export const RESET_EDIT_SHORT_URL_TAGS = 'shlink/shortUrlTags/RESET_EDIT_SHORT_URL_TAGS';
|
|
||||||
export const SHORT_URL_TAGS_EDITED = 'shlink/shortUrlTags/SHORT_URL_TAGS_EDITED';
|
export const SHORT_URL_TAGS_EDITED = 'shlink/shortUrlTags/SHORT_URL_TAGS_EDITED';
|
||||||
|
export const RESET_EDIT_SHORT_URL_TAGS = 'shlink/shortUrlTags/RESET_EDIT_SHORT_URL_TAGS';
|
||||||
/* eslint-enable padding-line-between-statements */
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
export const shortUrlTagsType = PropTypes.shape({
|
export const shortUrlTagsType = PropTypes.shape({
|
||||||
|
@ -26,7 +25,7 @@ const initialState = {
|
||||||
export default handleActions({
|
export default handleActions({
|
||||||
[EDIT_SHORT_URL_TAGS_START]: (state) => ({ ...state, saving: true, error: false }),
|
[EDIT_SHORT_URL_TAGS_START]: (state) => ({ ...state, saving: true, error: false }),
|
||||||
[EDIT_SHORT_URL_TAGS_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
[EDIT_SHORT_URL_TAGS_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
||||||
[EDIT_SHORT_URL_TAGS]: (state, { shortCode, tags }) => ({ shortCode, tags, saving: false, error: false }),
|
[SHORT_URL_TAGS_EDITED]: (state, { shortCode, tags }) => ({ shortCode, tags, saving: false, error: false }),
|
||||||
[RESET_EDIT_SHORT_URL_TAGS]: () => initialState,
|
[RESET_EDIT_SHORT_URL_TAGS]: () => initialState,
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ export const editShortUrlTags = (buildShlinkApiClient) => (shortCode, tags) => a
|
||||||
try {
|
try {
|
||||||
const normalizedTags = await updateShortUrlTags(shortCode, tags);
|
const normalizedTags = await updateShortUrlTags(shortCode, tags);
|
||||||
|
|
||||||
dispatch({ tags: normalizedTags, shortCode, type: EDIT_SHORT_URL_TAGS });
|
dispatch({ tags: normalizedTags, shortCode, type: SHORT_URL_TAGS_EDITED });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dispatch({ type: EDIT_SHORT_URL_TAGS_ERROR });
|
dispatch({ type: EDIT_SHORT_URL_TAGS_ERROR });
|
||||||
|
|
||||||
|
@ -46,9 +45,3 @@ export const editShortUrlTags = (buildShlinkApiClient) => (shortCode, tags) => a
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetShortUrlsTags = createAction(RESET_EDIT_SHORT_URL_TAGS);
|
export const resetShortUrlsTags = createAction(RESET_EDIT_SHORT_URL_TAGS);
|
||||||
|
|
||||||
export const shortUrlTagsEdited = (shortCode, tags) => ({
|
|
||||||
tags,
|
|
||||||
shortCode,
|
|
||||||
type: SHORT_URL_TAGS_EDITED,
|
|
||||||
});
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import CreateShortUrlResult from '../helpers/CreateShortUrlResult';
|
||||||
import { listShortUrls } from '../reducers/shortUrlsList';
|
import { listShortUrls } from '../reducers/shortUrlsList';
|
||||||
import { createShortUrl, resetCreateShortUrl } from '../reducers/shortUrlCreation';
|
import { createShortUrl, resetCreateShortUrl } from '../reducers/shortUrlCreation';
|
||||||
import { deleteShortUrl, resetDeleteShortUrl, shortUrlDeleted } from '../reducers/shortUrlDeletion';
|
import { deleteShortUrl, resetDeleteShortUrl, shortUrlDeleted } from '../reducers/shortUrlDeletion';
|
||||||
import { editShortUrlTags, resetShortUrlsTags, shortUrlTagsEdited } from '../reducers/shortUrlTags';
|
import { editShortUrlTags, resetShortUrlsTags } from '../reducers/shortUrlTags';
|
||||||
import { editShortUrlMeta, resetShortUrlMeta } from '../reducers/shortUrlMeta';
|
import { editShortUrlMeta, resetShortUrlMeta } from '../reducers/shortUrlMeta';
|
||||||
import { resetShortUrlParams } from '../reducers/shortUrlsListParams';
|
import { resetShortUrlParams } from '../reducers/shortUrlsListParams';
|
||||||
|
|
||||||
|
@ -58,10 +58,7 @@ const provideServices = (bottle, connect) => {
|
||||||
));
|
));
|
||||||
|
|
||||||
bottle.serviceFactory('EditTagsModal', EditTagsModal, 'TagsSelector');
|
bottle.serviceFactory('EditTagsModal', EditTagsModal, 'TagsSelector');
|
||||||
bottle.decorator('EditTagsModal', connect(
|
bottle.decorator('EditTagsModal', connect([ 'shortUrlTags' ], [ 'editShortUrlTags', 'resetShortUrlsTags' ]));
|
||||||
[ 'shortUrlTags' ],
|
|
||||||
[ 'editShortUrlTags', 'resetShortUrlsTags', 'shortUrlTagsEdited' ]
|
|
||||||
));
|
|
||||||
|
|
||||||
bottle.serviceFactory('EditMetaModal', () => EditMetaModal);
|
bottle.serviceFactory('EditMetaModal', () => EditMetaModal);
|
||||||
bottle.decorator('EditMetaModal', connect([ 'shortUrlMeta' ], [ 'editShortUrlMeta', 'resetShortUrlMeta' ]));
|
bottle.decorator('EditMetaModal', connect([ 'shortUrlMeta' ], [ 'editShortUrlMeta', 'resetShortUrlMeta' ]));
|
||||||
|
@ -69,7 +66,6 @@ const provideServices = (bottle, connect) => {
|
||||||
// Actions
|
// Actions
|
||||||
bottle.serviceFactory('editShortUrlTags', editShortUrlTags, 'buildShlinkApiClient');
|
bottle.serviceFactory('editShortUrlTags', editShortUrlTags, 'buildShlinkApiClient');
|
||||||
bottle.serviceFactory('resetShortUrlsTags', () => resetShortUrlsTags);
|
bottle.serviceFactory('resetShortUrlsTags', () => resetShortUrlsTags);
|
||||||
bottle.serviceFactory('shortUrlTagsEdited', () => shortUrlTagsEdited);
|
|
||||||
|
|
||||||
bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient');
|
bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient');
|
||||||
bottle.serviceFactory('resetShortUrlParams', () => resetShortUrlParams);
|
bottle.serviceFactory('resetShortUrlParams', () => resetShortUrlParams);
|
||||||
|
|
|
@ -8,7 +8,6 @@ describe('<EditTagsModal />', () => {
|
||||||
const shortCode = 'abc123';
|
const shortCode = 'abc123';
|
||||||
const TagsSelector = () => '';
|
const TagsSelector = () => '';
|
||||||
const editShortUrlTags = jest.fn(() => Promise.resolve());
|
const editShortUrlTags = jest.fn(() => Promise.resolve());
|
||||||
const shortUrlTagsEdited = jest.fn();
|
|
||||||
const resetShortUrlsTags = jest.fn();
|
const resetShortUrlsTags = jest.fn();
|
||||||
const toggle = jest.fn();
|
const toggle = jest.fn();
|
||||||
const createWrapper = (shortUrlTags) => {
|
const createWrapper = (shortUrlTags) => {
|
||||||
|
@ -25,7 +24,6 @@ describe('<EditTagsModal />', () => {
|
||||||
shortUrlTags={shortUrlTags}
|
shortUrlTags={shortUrlTags}
|
||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
editShortUrlTags={editShortUrlTags}
|
editShortUrlTags={editShortUrlTags}
|
||||||
shortUrlTagsEdited={shortUrlTagsEdited}
|
|
||||||
resetShortUrlsTags={resetShortUrlsTags}
|
resetShortUrlsTags={resetShortUrlsTags}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -107,28 +105,7 @@ describe('<EditTagsModal />', () => {
|
||||||
const modal = wrapper.find(Modal);
|
const modal = wrapper.find(Modal);
|
||||||
|
|
||||||
modal.simulate('closed');
|
modal.simulate('closed');
|
||||||
expect(shortUrlTagsEdited).not.toHaveBeenCalled();
|
expect(editShortUrlTags).not.toHaveBeenCalled();
|
||||||
});
|
|
||||||
|
|
||||||
it('notifies tags have been edited when window is closed after saving', (done) => {
|
|
||||||
const wrapper = createWrapper({
|
|
||||||
shortCode,
|
|
||||||
tags: [],
|
|
||||||
saving: true,
|
|
||||||
error: false,
|
|
||||||
});
|
|
||||||
const saveBtn = wrapper.find('.btn-primary');
|
|
||||||
const modal = wrapper.find(Modal);
|
|
||||||
|
|
||||||
saveBtn.simulate('click');
|
|
||||||
|
|
||||||
// Wrap this expect in a setImmediate since it is called as a result of an inner promise
|
|
||||||
setImmediate(() => {
|
|
||||||
modal.simulate('closed');
|
|
||||||
expect(shortUrlTagsEdited).toHaveBeenCalledTimes(1);
|
|
||||||
expect(shortUrlTagsEdited).toHaveBeenCalledWith(shortCode, []);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('toggles modal when cancel button is clicked', () => {
|
it('toggles modal when cancel button is clicked', () => {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import reducer, {
|
import reducer, {
|
||||||
EDIT_SHORT_URL_TAGS,
|
|
||||||
EDIT_SHORT_URL_TAGS_ERROR,
|
EDIT_SHORT_URL_TAGS_ERROR,
|
||||||
EDIT_SHORT_URL_TAGS_START,
|
EDIT_SHORT_URL_TAGS_START,
|
||||||
RESET_EDIT_SHORT_URL_TAGS,
|
RESET_EDIT_SHORT_URL_TAGS,
|
||||||
resetShortUrlsTags,
|
resetShortUrlsTags,
|
||||||
SHORT_URL_TAGS_EDITED,
|
SHORT_URL_TAGS_EDITED,
|
||||||
editShortUrlTags,
|
editShortUrlTags,
|
||||||
shortUrlTagsEdited,
|
|
||||||
} from '../../../src/short-urls/reducers/shortUrlTags';
|
} from '../../../src/short-urls/reducers/shortUrlTags';
|
||||||
|
|
||||||
describe('shortUrlTagsReducer', () => {
|
describe('shortUrlTagsReducer', () => {
|
||||||
|
@ -28,8 +26,8 @@ describe('shortUrlTagsReducer', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns provided tags and shortCode on EDIT_SHORT_URL_TAGS', () => {
|
it('returns provided tags and shortCode on SHORT_URL_TAGS_EDITED', () => {
|
||||||
expect(reducer({}, { type: EDIT_SHORT_URL_TAGS, tags, shortCode })).toEqual({
|
expect(reducer({}, { type: SHORT_URL_TAGS_EDITED, tags, shortCode })).toEqual({
|
||||||
tags,
|
tags,
|
||||||
shortCode,
|
shortCode,
|
||||||
saving: false,
|
saving: false,
|
||||||
|
@ -51,14 +49,6 @@ describe('shortUrlTagsReducer', () => {
|
||||||
it('creates expected action', () => expect(resetShortUrlsTags()).toEqual({ type: RESET_EDIT_SHORT_URL_TAGS }));
|
it('creates expected action', () => expect(resetShortUrlsTags()).toEqual({ type: RESET_EDIT_SHORT_URL_TAGS }));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('shortUrlTagsEdited', () => {
|
|
||||||
it('creates expected action', () => expect(shortUrlTagsEdited(shortCode, tags)).toEqual({
|
|
||||||
tags,
|
|
||||||
shortCode,
|
|
||||||
type: SHORT_URL_TAGS_EDITED,
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('editShortUrlTags', () => {
|
describe('editShortUrlTags', () => {
|
||||||
const updateShortUrlTags = jest.fn();
|
const updateShortUrlTags = jest.fn();
|
||||||
const buildShlinkApiClient = jest.fn().mockResolvedValue({ updateShortUrlTags });
|
const buildShlinkApiClient = jest.fn().mockResolvedValue({ updateShortUrlTags });
|
||||||
|
@ -82,7 +72,7 @@ describe('shortUrlTagsReducer', () => {
|
||||||
expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, tags);
|
expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, tags);
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_TAGS, tags: normalizedTags, shortCode });
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_TAGS_EDITED, tags: normalizedTags, shortCode });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches error on failure', async () => {
|
it('dispatches error on failure', async () => {
|
||||||
|
|
Loading…
Reference in a new issue