diff --git a/src/short-urls/ShortUrlsList.scss b/src/short-urls/ShortUrlsList.scss deleted file mode 100644 index 5b1be099..00000000 --- a/src/short-urls/ShortUrlsList.scss +++ /dev/null @@ -1,3 +0,0 @@ -.short-urls-list__header-icon { - margin-left: .4rem; -} diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index 855d2e52..e6a5eedc 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -5,7 +5,7 @@ import { FC, useEffect, useState } from 'react'; import { RouteComponentProps } from 'react-router'; import { Card } from 'reactstrap'; import SortingDropdown from '../utils/SortingDropdown'; -import { determineOrderDir, OrderDir } from '../utils/helpers/ordering'; +import { determineOrderDir, Order, OrderDir } from '../utils/helpers/ordering'; import { getServerId, SelectedServer } from '../servers/data'; import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub'; import { parseQuery } from '../utils/helpers/query'; @@ -14,7 +14,6 @@ import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList'; import { OrderableFields, ShortUrlsListParams, SORTABLE_FIELDS } from './reducers/shortUrlsListParams'; import { ShortUrlsTableProps } from './ShortUrlsTable'; import Paginator from './Paginator'; -import './ShortUrlsList.scss'; interface RouteParams { page: string; @@ -29,6 +28,8 @@ export interface ShortUrlsListProps extends RouteComponentProps { resetShortUrlParams: () => void; } +type ShortUrlsOrder = Order; + const ShortUrlsList = (ShortUrlsTable: FC) => boundToMercureHub(({ listShortUrls, resetShortUrlParams, @@ -39,34 +40,20 @@ const ShortUrlsList = (ShortUrlsTable: FC) => boundToMercur selectedServer, }: ShortUrlsListProps) => { const { orderBy } = shortUrlsListParams; - const [ order, setOrder ] = useState<{ orderField?: OrderableFields; orderDir?: OrderDir }>({ - orderField: orderBy && (head(keys(orderBy)) as OrderableFields), - orderDir: orderBy && head(values(orderBy)), + const [ order, setOrder ] = useState({ + field: orderBy && (head(keys(orderBy)) as OrderableFields), + dir: orderBy && head(values(orderBy)), }); const { pagination } = shortUrlsList?.shortUrls ?? {}; const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams }); - const handleOrderBy = (orderField?: OrderableFields, orderDir?: OrderDir) => { - setOrder({ orderField, orderDir }); - refreshList({ orderBy: orderField ? { [orderField]: orderDir } : undefined }); + const handleOrderBy = (field?: OrderableFields, dir?: OrderDir) => { + setOrder({ field, dir }); + refreshList({ orderBy: field ? { [field]: dir } : undefined }); }; const orderByColumn = (field: OrderableFields) => () => - handleOrderBy(field, determineOrderDir(field, order.orderField, order.orderDir)); - const renderOrderIcon = (field: OrderableFields) => { - if (order.orderField !== field) { - return null; - } - - if (!order.orderDir) { - return null; - } - - return ( - - ); - }; + handleOrderBy(field, determineOrderDir(field, order.field, order.dir)); + const renderOrderIcon = (field: OrderableFields) => order.dir && order.field === field && + ; useEffect(() => { const { tag } = parseQuery<{ tag?: string }>(location.search); @@ -82,8 +69,8 @@ const ShortUrlsList = (ShortUrlsTable: FC) => boundToMercur
diff --git a/src/short-urls/ShortUrlsTable.tsx b/src/short-urls/ShortUrlsTable.tsx index acf5d8fd..5a51db85 100644 --- a/src/short-urls/ShortUrlsTable.tsx +++ b/src/short-urls/ShortUrlsTable.tsx @@ -63,34 +63,29 @@ export const ShortUrlsTable = (ShortUrlsRow: FC) => ({ - Created at - {renderOrderIcon?.('dateCreated')} + Created at {renderOrderIcon?.('dateCreated')} - Short URL - {renderOrderIcon?.('shortCode')} + Short URL {renderOrderIcon?.('shortCode')} {!supportsTitle && ( - Long URL - {renderOrderIcon?.('longUrl')} + Long URL {renderOrderIcon?.('longUrl')} ) || ( - Title - {renderOrderIcon?.('title')} + Title {renderOrderIcon?.('title')}   /   - Long URL - {renderOrderIcon?.('longUrl')} + Long URL {renderOrderIcon?.('longUrl')} )} Tags - Visits{renderOrderIcon?.('visits')} + Visits {renderOrderIcon?.('visits')}   diff --git a/src/tags/TagsTable.tsx b/src/tags/TagsTable.tsx index 2208f669..e81d59c9 100644 --- a/src/tags/TagsTable.tsx +++ b/src/tags/TagsTable.tsx @@ -1,11 +1,13 @@ import { FC, useEffect, useMemo, useRef, useState } from 'react'; -import { splitEvery } from 'ramda'; +import { pipe, splitEvery } from 'ramda'; import { RouteChildrenProps } from 'react-router'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCaretDown as caretDownIcon, faCaretUp as caretUpIcon } from '@fortawesome/free-solid-svg-icons'; import { SimpleCard } from '../utils/SimpleCard'; import SimplePaginator from '../common/SimplePaginator'; import { useQueryState } from '../utils/helpers/hooks'; import { parseQuery } from '../utils/helpers/query'; -import { Order, sortList } from '../utils/helpers/ordering'; +import { determineOrderDir, Order, sortList } from '../utils/helpers/ordering'; import { TagsListChildrenProps } from './data/TagsListChildrenProps'; import { TagsTableRowProps } from './TagsTableRow'; import { NormalizedTag } from './data'; @@ -22,20 +24,27 @@ export const TagsTable = (TagsTableRow: FC) => ( const isFirstLoad = useRef(true); const { page: pageFromQuery = 1 } = parseQuery<{ page?: number | string }>(location.search); const [ page, setPage ] = useQueryState('page', Number(pageFromQuery)); - const [ order ] = useState({}); - const normalizedTags = useMemo( - () => tagsList.filteredTags.map((tag): NormalizedTag => ({ - tag, - shortUrls: tagsList.stats[tag]?.shortUrlsCount ?? 0, - visits: tagsList.stats[tag]?.visitsCount ?? 0, - })), - [ tagsList.filteredTags ], + const [ order, setOrder ] = useState({}); + const sortedTags = useMemo( + pipe( + () => tagsList.filteredTags.map((tag): NormalizedTag => ({ + tag, + shortUrls: tagsList.stats[tag]?.shortUrlsCount ?? 0, + visits: tagsList.stats[tag]?.visitsCount ?? 0, + })), + (normalizedTags) => sortList(normalizedTags, order), + ), + [ tagsList.filteredTags, order ], ); - const sortedTags = sortList(normalizedTags, order); const pages = splitEvery(TAGS_PER_PAGE, sortedTags); const showPaginator = pages.length > 1; const currentPage = pages[page - 1] ?? []; + const orderByColumn = (field: OrderableFields) => + () => setOrder({ field, dir: determineOrderDir(field, order.field, order.dir) }); + const renderOrderIcon = (field: OrderableFields) => order.dir && order.field === field && + ; + useEffect(() => { !isFirstLoad.current && setPage(1); isFirstLoad.current = false; @@ -49,9 +58,13 @@ export const TagsTable = (TagsTableRow: FC) => ( - - - + + + diff --git a/test/short-urls/ShortUrlsList.test.tsx b/test/short-urls/ShortUrlsList.test.tsx index f7286649..09ff55d4 100644 --- a/test/short-urls/ShortUrlsList.test.tsx +++ b/test/short-urls/ShortUrlsList.test.tsx @@ -79,13 +79,13 @@ describe('', () => { const renderIcon = (field: OrderableFields) => (wrapper.find(ShortUrlsTable).prop('renderOrderIcon') as (field: OrderableFields) => ReactElement | null)(field); - expect(renderIcon('visits')).toEqual(null); + expect(renderIcon('visits')).toEqual(undefined); wrapper.find(SortingDropdown).simulate('change', 'visits'); - expect(renderIcon('visits')).toEqual(null); + expect(renderIcon('visits')).toEqual(undefined); wrapper.find(SortingDropdown).simulate('change', 'visits', 'ASC'); - expect(renderIcon('visits')).not.toEqual(null); + expect(renderIcon('visits')).not.toEqual(undefined); }); it('handles order by through table', () => {
TagShort URLsVisitsTag {renderOrderIcon('tag')} + Short URLs {renderOrderIcon('shortUrls')} + + Visits {renderOrderIcon('visits')} +