mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Implemented dependency injection in all tag related components
This commit is contained in:
parent
79a0a5e4ea
commit
471322f4db
13 changed files with 75 additions and 81 deletions
|
@ -20,7 +20,6 @@ import SearchBar from '../short-urls/SearchBar';
|
|||
import { listShortUrls } from '../short-urls/reducers/shortUrlsList';
|
||||
import ShortUrlsList from '../short-urls/ShortUrlsList';
|
||||
import { resetShortUrlParams } from '../short-urls/reducers/shortUrlsListParams';
|
||||
import Tag from '../tags/helpers/Tag';
|
||||
import { ColorGenerator } from '../utils/ColorGenerator';
|
||||
import { Storage } from '../utils/Storage';
|
||||
import ShortUrlsRow from '../short-urls/helpers/ShortUrlsRow';
|
||||
|
@ -41,6 +40,11 @@ import { deleteShortUrl, resetDeleteShortUrl, shortUrlDeleted } from '../short-u
|
|||
import EditTagsModal from '../short-urls/helpers/EditTagsModal';
|
||||
import { editShortUrlTags, resetShortUrlsTags, shortUrlTagsEdited } from '../short-urls/reducers/shortUrlTags';
|
||||
import buildShlinkApiClient from '../api/ShlinkApiClientBuilder';
|
||||
import TagCard from '../tags/TagCard';
|
||||
import DeleteTagConfirmModal from '../tags/helpers/DeleteTagConfirmModal';
|
||||
import { deleteTag, tagDeleted } from '../tags/reducers/tagDelete';
|
||||
import EditTagModal from '../tags/helpers/EditTagModal';
|
||||
import { editTag, tagEdited } from '../tags/reducers/tagEdit';
|
||||
|
||||
const bottle = new Bottle();
|
||||
const { container } = bottle;
|
||||
|
@ -78,7 +82,7 @@ bottle.serviceFactory('App', App, 'MainHeader', 'Home', 'MenuLayout', 'CreateSer
|
|||
bottle.serviceFactory('ServersDropdown', ServersDropdown, 'ServersExporter');
|
||||
bottle.decorator('ServersDropdown', connectDecorator([ 'servers', 'selectedServer' ], { listServers, selectServer }));
|
||||
|
||||
bottle.serviceFactory('TagsList', () => TagsList);
|
||||
bottle.serviceFactory('TagsList', TagsList, 'TagCard');
|
||||
bottle.decorator('TagsList', connectDecorator([ 'tagsList' ], { forceListTags, filterTags }));
|
||||
|
||||
bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList');
|
||||
|
@ -86,7 +90,7 @@ bottle.decorator('ShortUrls', connect(
|
|||
(state) => assoc('shortUrlsList', state.shortUrlsList.shortUrls, state.shortUrlsList)
|
||||
));
|
||||
|
||||
bottle.serviceFactory('SearchBar', SearchBar, 'Tag');
|
||||
bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator');
|
||||
bottle.decorator('SearchBar', connectDecorator([ 'shortUrlsListParams' ], { listShortUrls }));
|
||||
|
||||
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsRow');
|
||||
|
@ -95,13 +99,11 @@ bottle.decorator('ShortUrlsList', connectDecorator(
|
|||
{ listShortUrls, resetShortUrlParams }
|
||||
));
|
||||
|
||||
bottle.serviceFactory('Tag', Tag, 'ColorGenerator');
|
||||
|
||||
bottle.constant('localStorage', global.localStorage);
|
||||
bottle.service('Storage', Storage, 'localStorage');
|
||||
bottle.service('ColorGenerator', ColorGenerator, 'Storage');
|
||||
|
||||
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'Tag', 'ShortUrlsRowMenu');
|
||||
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator');
|
||||
|
||||
bottle.serviceFactory('ShortUrlsRowMenu', ShortUrlsRowMenu, 'DeleteShortUrlModal', 'EditTagsModal');
|
||||
|
||||
|
@ -151,4 +153,12 @@ bottle.serviceFactory('shortUrlTagsEdited', () => shortUrlTagsEdited);
|
|||
|
||||
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'axios');
|
||||
|
||||
bottle.serviceFactory('TagCard', TagCard, 'DeleteTagConfirmModal', 'EditTagModal', 'ColorGenerator');
|
||||
|
||||
bottle.serviceFactory('DeleteTagConfirmModal', () => DeleteTagConfirmModal);
|
||||
bottle.decorator('DeleteTagConfirmModal', connectDecorator([ 'tagDelete' ], { deleteTag, tagDeleted }));
|
||||
|
||||
bottle.serviceFactory('EditTagModal', EditTagModal, 'ColorGenerator');
|
||||
bottle.decorator('EditTagModal', connectDecorator([ 'tagEdit' ], { editTag, tagEdited }));
|
||||
|
||||
export default container;
|
||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
import { isEmpty } from 'ramda';
|
||||
import PropTypes from 'prop-types';
|
||||
import SearchField from '../utils/SearchField';
|
||||
import Tag from '../tags/helpers/Tag';
|
||||
import { shortUrlsListParamsType } from './reducers/shortUrlsListParams';
|
||||
import './SearchBar.scss';
|
||||
|
||||
|
@ -12,7 +13,7 @@ const propTypes = {
|
|||
shortUrlsListParams: shortUrlsListParamsType,
|
||||
};
|
||||
|
||||
const SearchBar = (Tag) => {
|
||||
const SearchBar = (colorGenerator) => {
|
||||
const SearchBar = ({ listShortUrls, shortUrlsListParams }) => {
|
||||
const selectedTags = shortUrlsListParams.tags || [];
|
||||
|
||||
|
@ -29,6 +30,7 @@ const SearchBar = (Tag) => {
|
|||
|
||||
{selectedTags.map((tag) => (
|
||||
<Tag
|
||||
colorGenerator={colorGenerator}
|
||||
key={tag}
|
||||
text={tag}
|
||||
clearable
|
||||
|
|
|
@ -7,9 +7,10 @@ import { serverType } from '../../servers/prop-types';
|
|||
import ExternalLink from '../../utils/ExternalLink';
|
||||
import { shortUrlType } from '../reducers/shortUrlsList';
|
||||
import { stateFlagTimeout } from '../../utils/utils';
|
||||
import Tag from '../../tags/helpers/Tag';
|
||||
import './ShortUrlsRow.scss';
|
||||
|
||||
const ShortUrlsRow = (Tag, ShortUrlsRowMenu) => class ShortUrlsRow extends React.Component {
|
||||
const ShortUrlsRow = (ShortUrlsRowMenu, colorGenerator) => class ShortUrlsRow extends React.Component {
|
||||
static propTypes = {
|
||||
refreshList: PropTypes.func,
|
||||
shortUrlsListParams: shortUrlsListParamsType,
|
||||
|
@ -29,6 +30,7 @@ const ShortUrlsRow = (Tag, ShortUrlsRowMenu) => class ShortUrlsRow extends React
|
|||
|
||||
return tags.map((tag) => (
|
||||
<Tag
|
||||
colorGenerator={colorGenerator}
|
||||
key={tag}
|
||||
text={tag}
|
||||
onClick={() => refreshList({ tags: [ ...selectedTags, tag ] })}
|
||||
|
|
|
@ -7,10 +7,8 @@ import React from 'react';
|
|||
import { Link } from 'react-router-dom';
|
||||
import TagBullet from './helpers/TagBullet';
|
||||
import './TagCard.scss';
|
||||
import DeleteTagConfirmModal from './helpers/DeleteTagConfirmModal';
|
||||
import EditTagModal from './helpers/EditTagModal';
|
||||
|
||||
export default class TagCard extends React.Component {
|
||||
const TagCard = (DeleteTagConfirmModal, EditTagModal, colorGenerator) => class TagCard extends React.Component {
|
||||
static propTypes = {
|
||||
tag: PropTypes.string,
|
||||
currentServerId: PropTypes.string,
|
||||
|
@ -35,7 +33,7 @@ export default class TagCard extends React.Component {
|
|||
<FontAwesomeIcon icon={editIcon} />
|
||||
</button>
|
||||
<h5 className="tag-card__tag-title">
|
||||
<TagBullet tag={tag} />
|
||||
<TagBullet tag={tag} colorGenerator={colorGenerator} />
|
||||
<Link to={`/server/${currentServerId}/list-short-urls/1?tag=${tag}`}>{tag}</Link>
|
||||
</h5>
|
||||
</CardBody>
|
||||
|
@ -45,4 +43,6 @@ export default class TagCard extends React.Component {
|
|||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default TagCard;
|
||||
|
|
|
@ -3,12 +3,11 @@ import { splitEvery } from 'ramda';
|
|||
import PropTypes from 'prop-types';
|
||||
import MuttedMessage from '../utils/MuttedMessage';
|
||||
import SearchField from '../utils/SearchField';
|
||||
import TagCard from './TagCard';
|
||||
|
||||
const { ceil } = Math;
|
||||
const TAGS_GROUPS_AMOUNT = 4;
|
||||
|
||||
export default class TagsList extends React.Component {
|
||||
const TagsList = (TagCard) => class TagsList extends React.Component {
|
||||
static propTypes = {
|
||||
filterTags: PropTypes.func,
|
||||
forceListTags: PropTypes.func,
|
||||
|
@ -80,4 +79,6 @@ export default class TagsList extends React.Component {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default TagsList;
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
|
||||
import PropTypes from 'prop-types';
|
||||
import { pick } from 'ramda';
|
||||
import { deleteTag, tagDeleted, tagDeleteType } from '../reducers/tagDelete';
|
||||
import { tagDeleteType } from '../reducers/tagDelete';
|
||||
|
||||
export class DeleteTagConfirmModalComponent extends React.Component {
|
||||
export default class DeleteTagConfirmModal extends React.Component {
|
||||
static propTypes = {
|
||||
tag: PropTypes.string.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
|
@ -67,10 +65,3 @@ export class DeleteTagConfirmModalComponent extends React.Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const DeleteTagConfirmModal = connect(
|
||||
pick([ 'tagDelete' ]),
|
||||
{ deleteTag, tagDeleted }
|
||||
)(DeleteTagConfirmModalComponent);
|
||||
|
||||
export default DeleteTagConfirmModal;
|
||||
|
|
|
@ -1,31 +1,23 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Modal, ModalBody, ModalFooter, ModalHeader, Popover } from 'reactstrap';
|
||||
import { pick } from 'ramda';
|
||||
import { ChromePicker } from 'react-color';
|
||||
import colorIcon from '@fortawesome/fontawesome-free-solid/faPalette';
|
||||
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
|
||||
import PropTypes from 'prop-types';
|
||||
import colorGenerator, { colorGeneratorType } from '../../utils/ColorGenerator';
|
||||
import { editTag, tagEdited } from '../reducers/tagEdit';
|
||||
import './EditTagModal.scss';
|
||||
|
||||
export class EditTagModalComponent extends React.Component {
|
||||
const EditTagModal = ({ getColorForKey }) => class EditTagModal extends React.Component {
|
||||
static propTypes = {
|
||||
tag: PropTypes.string,
|
||||
editTag: PropTypes.func,
|
||||
toggle: PropTypes.func,
|
||||
tagEdited: PropTypes.func,
|
||||
colorGenerator: colorGeneratorType,
|
||||
isOpen: PropTypes.bool,
|
||||
tagEdit: PropTypes.shape({
|
||||
error: PropTypes.bool,
|
||||
editing: PropTypes.bool,
|
||||
}),
|
||||
};
|
||||
static defaultProps = {
|
||||
colorGenerator,
|
||||
};
|
||||
|
||||
saveTag = (e) => {
|
||||
e.preventDefault();
|
||||
|
@ -53,12 +45,12 @@ export class EditTagModalComponent extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { colorGenerator, tag } = props;
|
||||
const { tag } = props;
|
||||
|
||||
this.state = {
|
||||
showColorPicker: false,
|
||||
tag,
|
||||
color: colorGenerator.getColorForKey(tag),
|
||||
color: getColorForKey(tag),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -131,8 +123,6 @@ export class EditTagModalComponent extends React.Component {
|
|||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const EditTagModal = connect(pick([ 'tagEdit' ]), { editTag, tagEdited })(EditTagModalComponent);
|
||||
};
|
||||
|
||||
export default EditTagModal;
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './Tag.scss';
|
||||
import { colorGeneratorType } from '../../utils/ColorGenerator';
|
||||
|
||||
const propTypes = {
|
||||
text: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
clearable: PropTypes.bool,
|
||||
colorGenerator: colorGeneratorType,
|
||||
onClick: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
const Tag = (colorGenerator) => {
|
||||
const Tag = ({
|
||||
text,
|
||||
children,
|
||||
clearable,
|
||||
colorGenerator,
|
||||
onClick = () => {},
|
||||
onClose = () => {},
|
||||
}) => (
|
||||
|
@ -30,7 +32,4 @@ const Tag = (colorGenerator) => {
|
|||
|
||||
Tag.propTypes = propTypes;
|
||||
|
||||
return Tag;
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
import React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import colorGenerator, { colorGeneratorType } from '../../utils/ColorGenerator';
|
||||
import { colorGeneratorType } from '../../utils/ColorGenerator';
|
||||
import './TagBullet.scss';
|
||||
|
||||
const propTypes = {
|
||||
tag: PropTypes.string.isRequired,
|
||||
colorGenerator: colorGeneratorType,
|
||||
};
|
||||
const defaultProps = {
|
||||
colorGenerator,
|
||||
};
|
||||
|
||||
export default function TagBullet({ tag, colorGenerator }) {
|
||||
return (
|
||||
const TagBullet = ({ tag, colorGenerator }) => (
|
||||
<div
|
||||
style={{ backgroundColor: colorGenerator.getColorForKey(tag) }}
|
||||
className="tag-bullet"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
TagBullet.propTypes = propTypes;
|
||||
TagBullet.defaultProps = defaultProps;
|
||||
|
||||
export default TagBullet;
|
||||
|
|
|
@ -54,7 +54,7 @@ const TagsSelector = (colorGenerator) => class TagsSelector extends React.Compon
|
|||
getSuggestionValue={(suggestion) => suggestion}
|
||||
renderSuggestion={(suggestion) => (
|
||||
<React.Fragment>
|
||||
<TagBullet tag={suggestion} />
|
||||
<TagBullet tag={suggestion} colorGenerator={colorGenerator} />
|
||||
{suggestion}
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
|
|
@ -3,12 +3,12 @@ import { shallow } from 'enzyme';
|
|||
import sinon from 'sinon';
|
||||
import searchBarCreator from '../../src/short-urls/SearchBar';
|
||||
import SearchField from '../../src/utils/SearchField';
|
||||
import Tag from '../../src/tags/helpers/Tag';
|
||||
|
||||
describe('<SearchBar />', () => {
|
||||
let wrapper;
|
||||
const listShortUrlsMock = sinon.spy();
|
||||
const Tag = () => '';
|
||||
const SearchBar = searchBarCreator(Tag);
|
||||
const SearchBar = searchBarCreator({});
|
||||
|
||||
afterEach(() => {
|
||||
listShortUrlsMock.resetHistory();
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { Link } from 'react-router-dom';
|
||||
import TagCard from '../../src/tags/TagCard';
|
||||
import createTagCard from '../../src/tags/TagCard';
|
||||
import TagBullet from '../../src/tags/helpers/TagBullet';
|
||||
|
||||
describe('<TagCard />', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
const TagCard = createTagCard(() => '', () => '', {});
|
||||
|
||||
wrapper = shallow(<TagCard tag="ssr" currentServerId="1" />);
|
||||
});
|
||||
afterEach(() => wrapper.unmount());
|
||||
|
|
|
@ -2,16 +2,17 @@ import React from 'react';
|
|||
import { shallow } from 'enzyme';
|
||||
import { identity, range } from 'ramda';
|
||||
import * as sinon from 'sinon';
|
||||
import TagsList from '../../src/tags/TagsList';
|
||||
import createTagsList from '../../src/tags/TagsList';
|
||||
import MuttedMessage from '../../src/utils/MuttedMessage';
|
||||
import TagCard from '../../src/tags/TagCard';
|
||||
import SearchField from '../../src/utils/SearchField';
|
||||
|
||||
describe('<TagsList />', () => {
|
||||
let wrapper;
|
||||
const filterTags = sinon.spy();
|
||||
const TagCard = () => '';
|
||||
const createWrapper = (tagsList) => {
|
||||
const params = { serverId: '1' };
|
||||
const TagsList = createTagsList(TagCard);
|
||||
|
||||
wrapper = shallow(
|
||||
<TagsList forceListTags={identity} filterTags={filterTags} match={{ params }} tagsList={tagsList} />
|
||||
|
|
Loading…
Reference in a new issue