Minor changes on tags filtering for short URLs

This commit is contained in:
Alejandro Celaya 2021-11-07 11:03:31 +01:00
parent 303900756d
commit 109baef828
12 changed files with 19 additions and 68 deletions

View file

@ -13,7 +13,7 @@ import './MenuLayout.scss';
const MenuLayout = (
TagsList: FC,
ShortUrls: FC,
ShortUrlsList: FC,
AsideMenu: FC<AsideMenuProps>,
CreateShortUrl: FC,
ShortUrlVisits: FC,
@ -49,7 +49,7 @@ const MenuLayout = (
<Switch>
<Redirect exact from="/server/:serverId" to="/server/:serverId/overview" />
<Route exact path="/server/:serverId/overview" component={Overview} />
<Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrls} />
<Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrlsList} />
<Route exact path="/server/:serverId/create-short-url" component={CreateShortUrl} />
<Route path="/server/:serverId/short-code/:shortCode/visits" component={ShortUrlVisits} />
<Route path="/server/:serverId/short-code/:shortCode/edit" component={EditShortUrl} />

View file

@ -35,7 +35,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter:
'MenuLayout',
MenuLayout,
'TagsList',
'ShortUrls',
'ShortUrlsList',
'AsideMenu',
'CreateShortUrl',
'ShortUrlVisits',

View file

@ -36,7 +36,7 @@ const connect: ConnectDecorator = (propsFromState: string[] | null, actionServic
provideAppServices(bottle, connect);
provideCommonServices(bottle, connect, withRouter);
provideApiServices(bottle);
provideShortUrlsServices(bottle, connect);
provideShortUrlsServices(bottle, connect, withRouter);
provideServersServices(bottle, connect, withRouter);
provideTagsServices(bottle, connect);
provideVisitsServices(bottle, connect);

View file

@ -107,7 +107,7 @@ export const Overview = (
shortUrlsList={shortUrlsList}
selectedServer={selectedServer}
className="mb-0"
onTagClick={(tag) => history.push(`/server/${serverId}/list-short-urls/1?tag=${tag}`)}
onTagClick={(tag) => history.push(`/server/${serverId}/list-short-urls/1?tags=${encodeURIComponent(tag)}`)}
/>
</CardBody>
</Card>

View file

@ -1,23 +0,0 @@
import { FC, useEffect, useState } from 'react';
import { ShortUrlsListProps } from './ShortUrlsList';
const ShortUrls = (SearchBar: FC, ShortUrlsList: FC<ShortUrlsListProps>) => (props: ShortUrlsListProps) => {
const { match } = props;
const { page = '1', serverId = '' } = match?.params ?? {};
const [ urlsListKey, setUrlsListKey ] = useState(`${serverId}_${page}`);
// Using a key on a component makes react to create a new instance every time the key changes
// Without it, pagination on the URL will not make the component to be refreshed
useEffect(() => {
setUrlsListKey(`${serverId}_${page}`);
}, [ serverId, page ]);
return (
<>
<div className="form-group"><SearchBar /></div>
<ShortUrlsList {...props} key={urlsListKey} />
</>
);
};
export default ShortUrls;

View file

@ -1,5 +1,4 @@
import Bottle from 'bottlejs';
import ShortUrls from '../ShortUrls';
import Bottle, { Decorator } from 'bottlejs';
import SearchBar from '../SearchBar';
import ShortUrlsList from '../ShortUrlsList';
import ShortUrlsRow from '../helpers/ShortUrlsRow';
@ -19,14 +18,11 @@ import { ShortUrlForm } from '../ShortUrlForm';
import { EditShortUrl } from '../EditShortUrl';
import { getShortUrlDetail } from '../reducers/shortUrlDetail';
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter: Decorator) => {
// Components
bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList');
bottle.decorator('ShortUrls', connect([ 'shortUrlsList' ]));
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsTable');
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsTable', 'SearchBar');
bottle.decorator('ShortUrlsList', connect(
[ 'selectedServer', 'shortUrlsListParams', 'mercureInfo' ],
[ 'selectedServer', 'shortUrlsListParams', 'mercureInfo', 'shortUrlsList' ],
[ 'listShortUrls', 'resetShortUrlParams', 'createNewVisits', 'loadMercureInfo' ],
));
@ -57,6 +53,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
// Services
bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator');
bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ]));
bottle.decorator('SearchBar', withRouter);
// Actions
bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient');

View file

@ -61,7 +61,7 @@ const TagCard = (
<Collapse isOpen={displayed}>
<CardBody className="tag-card__body">
<Link
to={`/server/${serverId}/list-short-urls/1?tag=${encodeURIComponent(tag.tag)}`}
to={`/server/${serverId}/list-short-urls/1?tags=${encodeURIComponent(tag.tag)}`}
className="btn btn-outline-secondary btn-block d-flex justify-content-between align-items-center mb-1"
>
<span className="text-ellipsis"><FontAwesomeIcon icon={faLink} className="mr-2" />Short URLs</span>

View file

@ -32,7 +32,7 @@ export const TagsTableRow = (
<TagBullet tag={tag.tag} colorGenerator={colorGenerator} /> {tag.tag}
</th>
<td className="responsive-table__cell text-lg-right" data-th="Short URLs">
<Link to={`/server/${serverId}/list-short-urls/1?tag=${encodeURIComponent(tag.tag)}`}>
<Link to={`/server/${serverId}/list-short-urls/1?tags=${encodeURIComponent(tag.tag)}`}>
{prettify(tag.shortUrls)}
</Link>
</td>

View file

@ -12,10 +12,11 @@ interface SearchFieldProps {
className?: string;
large?: boolean;
noBorder?: boolean;
initialValue?: string;
}
const SearchField = ({ onChange, className, large = true, noBorder = false }: SearchFieldProps) => {
const [ searchTerm, setSearchTerm ] = useState('');
const SearchField = ({ onChange, className, large = true, noBorder = false, initialValue = '' }: SearchFieldProps) => {
const [ searchTerm, setSearchTerm ] = useState(initialValue);
const resetTimer = () => {
timer && clearTimeout(timer);

View file

@ -1,24 +0,0 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { Mock } from 'ts-mockery';
import shortUrlsCreator from '../../src/short-urls/ShortUrls';
import { ShortUrlsListProps } from '../../src/short-urls/ShortUrlsList';
describe('<ShortUrls />', () => {
let wrapper: ShallowWrapper;
const SearchBar = () => null;
const ShortUrlsList = () => null;
beforeEach(() => {
const ShortUrls = shortUrlsCreator(SearchBar, ShortUrlsList);
wrapper = shallow(
<ShortUrls {...Mock.all<ShortUrlsListProps>()} />,
);
});
afterEach(() => wrapper.unmount());
it('wraps a SearchBar and ShortUrlsList', () => {
expect(wrapper.find(SearchBar)).toHaveLength(1);
expect(wrapper.find(ShortUrlsList)).toHaveLength(1);
});
});

View file

@ -30,8 +30,8 @@ describe('<TagCard />', () => {
afterEach(jest.resetAllMocks);
it.each([
[ 'ssr', '/server/1/list-short-urls/1?tag=ssr' ],
[ 'ssr-&-foo', '/server/1/list-short-urls/1?tag=ssr-%26-foo' ],
[ 'ssr', '/server/1/list-short-urls/1?tags=ssr' ],
[ 'ssr-&-foo', '/server/1/list-short-urls/1?tags=ssr-%26-foo' ],
])('shows a TagBullet and a link to the list filtering by the tag', (tag, expectedLink) => {
const wrapper = createWrapper(tag);
const links = wrapper.find(Link);
@ -61,7 +61,7 @@ describe('<TagCard />', () => {
const links = wrapper.find(Link);
expect(links).toHaveLength(2);
expect(links.at(0).prop('to')).toEqual('/server/1/list-short-urls/1?tag=ssr');
expect(links.at(0).prop('to')).toEqual('/server/1/list-short-urls/1?tags=ssr');
expect(links.at(0).text()).toContain('48');
expect(links.at(1).prop('to')).toEqual('/server/1/tag/ssr/visits');
expect(links.at(1).text()).toContain('23,257');

View file

@ -35,7 +35,7 @@ describe('<TagsTableRow />', () => {
const visitsLink = links.last();
expect(shortUrlsLink.prop('children')).toEqual(expectedShortUrls);
expect(shortUrlsLink.prop('to')).toEqual(`/server/abc123/list-short-urls/1?tag=${encodeURIComponent('foo&bar')}`);
expect(shortUrlsLink.prop('to')).toEqual(`/server/abc123/list-short-urls/1?tags=${encodeURIComponent('foo&bar')}`);
expect(visitsLink.prop('children')).toEqual(expectedVisits);
expect(visitsLink.prop('to')).toEqual('/server/abc123/tag/foo&bar/visits');
});