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 = ( const MenuLayout = (
TagsList: FC, TagsList: FC,
ShortUrls: FC, ShortUrlsList: FC,
AsideMenu: FC<AsideMenuProps>, AsideMenu: FC<AsideMenuProps>,
CreateShortUrl: FC, CreateShortUrl: FC,
ShortUrlVisits: FC, ShortUrlVisits: FC,
@ -49,7 +49,7 @@ const MenuLayout = (
<Switch> <Switch>
<Redirect exact from="/server/:serverId" to="/server/:serverId/overview" /> <Redirect exact from="/server/:serverId" to="/server/:serverId/overview" />
<Route exact path="/server/:serverId/overview" component={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 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/visits" component={ShortUrlVisits} />
<Route path="/server/:serverId/short-code/:shortCode/edit" component={EditShortUrl} /> <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',
MenuLayout, MenuLayout,
'TagsList', 'TagsList',
'ShortUrls', 'ShortUrlsList',
'AsideMenu', 'AsideMenu',
'CreateShortUrl', 'CreateShortUrl',
'ShortUrlVisits', 'ShortUrlVisits',

View file

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

View file

@ -107,7 +107,7 @@ export const Overview = (
shortUrlsList={shortUrlsList} shortUrlsList={shortUrlsList}
selectedServer={selectedServer} selectedServer={selectedServer}
className="mb-0" 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> </CardBody>
</Card> </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 Bottle, { Decorator } from 'bottlejs';
import ShortUrls from '../ShortUrls';
import SearchBar from '../SearchBar'; import SearchBar from '../SearchBar';
import ShortUrlsList from '../ShortUrlsList'; import ShortUrlsList from '../ShortUrlsList';
import ShortUrlsRow from '../helpers/ShortUrlsRow'; import ShortUrlsRow from '../helpers/ShortUrlsRow';
@ -19,14 +18,11 @@ import { ShortUrlForm } from '../ShortUrlForm';
import { EditShortUrl } from '../EditShortUrl'; import { EditShortUrl } from '../EditShortUrl';
import { getShortUrlDetail } from '../reducers/shortUrlDetail'; import { getShortUrlDetail } from '../reducers/shortUrlDetail';
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter: Decorator) => {
// Components // Components
bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList'); bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsTable', 'SearchBar');
bottle.decorator('ShortUrls', connect([ 'shortUrlsList' ]));
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsTable');
bottle.decorator('ShortUrlsList', connect( bottle.decorator('ShortUrlsList', connect(
[ 'selectedServer', 'shortUrlsListParams', 'mercureInfo' ], [ 'selectedServer', 'shortUrlsListParams', 'mercureInfo', 'shortUrlsList' ],
[ 'listShortUrls', 'resetShortUrlParams', 'createNewVisits', 'loadMercureInfo' ], [ 'listShortUrls', 'resetShortUrlParams', 'createNewVisits', 'loadMercureInfo' ],
)); ));
@ -57,6 +53,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
// Services // Services
bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator'); bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator');
bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ])); bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ]));
bottle.decorator('SearchBar', withRouter);
// Actions // Actions
bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient'); bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient');

View file

@ -61,7 +61,7 @@ const TagCard = (
<Collapse isOpen={displayed}> <Collapse isOpen={displayed}>
<CardBody className="tag-card__body"> <CardBody className="tag-card__body">
<Link <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" 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> <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} <TagBullet tag={tag.tag} colorGenerator={colorGenerator} /> {tag.tag}
</th> </th>
<td className="responsive-table__cell text-lg-right" data-th="Short URLs"> <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)} {prettify(tag.shortUrls)}
</Link> </Link>
</td> </td>

View file

@ -12,10 +12,11 @@ interface SearchFieldProps {
className?: string; className?: string;
large?: boolean; large?: boolean;
noBorder?: boolean; noBorder?: boolean;
initialValue?: string;
} }
const SearchField = ({ onChange, className, large = true, noBorder = false }: SearchFieldProps) => { const SearchField = ({ onChange, className, large = true, noBorder = false, initialValue = '' }: SearchFieldProps) => {
const [ searchTerm, setSearchTerm ] = useState(''); const [ searchTerm, setSearchTerm ] = useState(initialValue);
const resetTimer = () => { const resetTimer = () => {
timer && clearTimeout(timer); 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); afterEach(jest.resetAllMocks);
it.each([ it.each([
[ 'ssr', '/server/1/list-short-urls/1?tag=ssr' ], [ 'ssr', '/server/1/list-short-urls/1?tags=ssr' ],
[ 'ssr-&-foo', '/server/1/list-short-urls/1?tag=ssr-%26-foo' ], [ '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) => { ])('shows a TagBullet and a link to the list filtering by the tag', (tag, expectedLink) => {
const wrapper = createWrapper(tag); const wrapper = createWrapper(tag);
const links = wrapper.find(Link); const links = wrapper.find(Link);
@ -61,7 +61,7 @@ describe('<TagCard />', () => {
const links = wrapper.find(Link); const links = wrapper.find(Link);
expect(links).toHaveLength(2); 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(0).text()).toContain('48');
expect(links.at(1).prop('to')).toEqual('/server/1/tag/ssr/visits'); expect(links.at(1).prop('to')).toEqual('/server/1/tag/ssr/visits');
expect(links.at(1).text()).toContain('23,257'); expect(links.at(1).text()).toContain('23,257');

View file

@ -35,7 +35,7 @@ describe('<TagsTableRow />', () => {
const visitsLink = links.last(); const visitsLink = links.last();
expect(shortUrlsLink.prop('children')).toEqual(expectedShortUrls); 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('children')).toEqual(expectedVisits);
expect(visitsLink.prop('to')).toEqual('/server/abc123/tag/foo&bar/visits'); expect(visitsLink.prop('to')).toEqual('/server/abc123/tag/foo&bar/visits');
}); });