diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index e6a5eedc..8f3ed9e2 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -67,12 +67,7 @@ const ShortUrlsList = (ShortUrlsTable: FC) => boundToMercur return ( <>
- +
, TagsTable: FC - () => setOrder({ field, dir: determineOrderDir(field, order.field, order.dir) }); + const orderByColumn = (field: OrderableFields) => () => { + const dir = determineOrderDir(field, order.field, order.dir); + + setOrder({ field: dir ? field : undefined, dir }); + }; const renderContent = () => { if (tagsList.filteredTags.length < 1) { @@ -85,12 +88,7 @@ const TagsList = (TagsCards: FC, TagsTable: FC
- setOrder({ field, dir })} - /> + setOrder({ field, dir })} />
{renderContent()} diff --git a/src/utils/SortingDropdown.tsx b/src/utils/SortingDropdown.tsx index bba0bed7..ebc04d2c 100644 --- a/src/utils/SortingDropdown.tsx +++ b/src/utils/SortingDropdown.tsx @@ -3,23 +3,22 @@ import { toPairs } from 'ramda'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSortAmountUp as sortAscIcon, faSortAmountDown as sortDescIcon } from '@fortawesome/free-solid-svg-icons'; import classNames from 'classnames'; -import { determineOrderDir, OrderDir } from './helpers/ordering'; +import { determineOrderDir, Order, OrderDir } from './helpers/ordering'; import './SortingDropdown.scss'; export interface SortingDropdownProps { items: Record; - orderField?: T; - orderDir?: OrderDir; + order: Order; onChange: (orderField?: T, orderDir?: OrderDir) => void; isButton?: boolean; right?: boolean; } export default function SortingDropdown( - { items, orderField, orderDir, onChange, isButton = true, right = false }: SortingDropdownProps, + { items, order, onChange, isButton = true, right = false }: SortingDropdownProps, ) { const handleItemClick = (fieldKey: T) => () => { - const newOrderDir = determineOrderDir(fieldKey, orderField, orderDir); + const newOrderDir = determineOrderDir(fieldKey, order.field, order.dir); onChange(newOrderDir ? fieldKey : undefined, newOrderDir); }; @@ -32,26 +31,26 @@ export default function SortingDropdown( className={classNames({ 'dropdown-btn__toggle btn-block': isButton, 'btn-sm p-0': !isButton })} > {!isButton && <>Order by} - {isButton && !orderField && <>Order by...} - {isButton && orderField && `Order by: "${items[orderField]}" - "${orderDir ?? 'DESC'}"`} + {isButton && !order.field && <>Order by...} + {isButton && order.field && `Order by: "${items[order.field]}" - "${order.dir ?? 'DESC'}"`} {toPairs(items).map(([ fieldKey, fieldValue ]) => ( - + {fieldValue} - {orderField === fieldKey && ( + {order.field === fieldKey && ( )} ))} - onChange()}> + onChange()}> Clear selection diff --git a/src/visits/charts/SortableBarChartCard.tsx b/src/visits/charts/SortableBarChartCard.tsx index b300af18..a627e514 100644 --- a/src/visits/charts/SortableBarChartCard.tsx +++ b/src/visits/charts/SortableBarChartCard.tsx @@ -1,7 +1,7 @@ import { FC, useState } from 'react'; import { fromPairs, pipe, reverse, sortBy, splitEvery, toLower, toPairs, type, zipObj } from 'ramda'; import { rangeOf } from '../../utils/utils'; -import { OrderDir } from '../../utils/helpers/ordering'; +import { Order } from '../../utils/helpers/ordering'; import SimplePaginator from '../../common/SimplePaginator'; import { roundTen } from '../../utils/helpers/numbers'; import SortingDropdown from '../../utils/SortingDropdown'; @@ -30,24 +30,21 @@ export const SortableBarChartCard: FC = ({ withPagination = true, ...rest }) => { - const [ order, setOrder ] = useState<{ orderField?: string; orderDir?: OrderDir }>({ - orderField: undefined, - orderDir: undefined, - }); + const [ order, setOrder ] = useState>({}); const [ currentPage, setCurrentPage ] = useState(1); const [ itemsPerPage, setItemsPerPage ] = useState(50); const getSortedPairsForStats = (stats: Stats, sortingItems: Record) => { const pairs = toPairs(stats); - const sortedPairs = !order.orderField ? pairs : sortBy( + const sortedPairs = !order.field ? pairs : sortBy( pipe( - order.orderField === Object.keys(sortingItems)[0] ? pickKeyFromPair : pickValueFromPair, + order.field === Object.keys(sortingItems)[0] ? pickKeyFromPair : pickValueFromPair, toLowerIfString, ), pairs, ); - return !order.orderDir || order.orderDir === 'ASC' ? sortedPairs : reverse(sortedPairs); + return !order.dir || order.dir === 'ASC' ? sortedPairs : reverse(sortedPairs); }; const determineCurrentPagePairs = (pages: StatsRow[][]): StatsRow[] => { const page = pages[currentPage - 1]; @@ -103,10 +100,9 @@ export const SortableBarChartCard: FC = ({ isButton={false} right items={sortingItems} - orderField={order.orderField} - orderDir={order.orderDir} - onChange={(orderField, orderDir) => { - setOrder({ orderField, orderDir }); + order={order} + onChange={(field, dir) => { + setOrder({ field, dir }); setCurrentPage(1); }} /> diff --git a/test/short-urls/ShortUrlsList.test.tsx b/test/short-urls/ShortUrlsList.test.tsx index 09ff55d4..e000a09f 100644 --- a/test/short-urls/ShortUrlsList.test.tsx +++ b/test/short-urls/ShortUrlsList.test.tsx @@ -108,23 +108,16 @@ describe('', () => { }); it('handles order by through dropdown', () => { - expect(wrapper.find(SortingDropdown).prop('orderField')).not.toBeDefined(); - expect(wrapper.find(SortingDropdown).prop('orderDir')).not.toBeDefined(); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({}); wrapper.find(SortingDropdown).simulate('change', 'visits', 'ASC'); - - expect(wrapper.find(SortingDropdown).prop('orderField')).toEqual('visits'); - expect(wrapper.find(SortingDropdown).prop('orderDir')).toEqual('ASC'); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({ field: 'visits', dir: 'ASC' }); wrapper.find(SortingDropdown).simulate('change', 'shortCode', 'DESC'); - - expect(wrapper.find(SortingDropdown).prop('orderField')).toEqual('shortCode'); - expect(wrapper.find(SortingDropdown).prop('orderDir')).toEqual('DESC'); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({ field: 'shortCode', dir: 'DESC' }); wrapper.find(SortingDropdown).simulate('change', undefined, undefined); - - expect(wrapper.find(SortingDropdown).prop('orderField')).toEqual(undefined); - expect(wrapper.find(SortingDropdown).prop('orderDir')).toEqual(undefined); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({}); expect(listShortUrlsMock).toHaveBeenCalledTimes(3); expect(listShortUrlsMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ @@ -140,10 +133,9 @@ describe('', () => { [ Mock.of({ visits: 'ASC' }), 'visits', 'ASC' ], [ Mock.of({ title: 'DESC' }), 'title', 'DESC' ], [ Mock.of(), undefined, undefined ], - ])('has expected initial ordering', (initialOrderBy, expectedField, expectedDir) => { + ])('has expected initial ordering', (initialOrderBy, field, dir) => { const wrapper = createWrapper(initialOrderBy); - expect(wrapper.find(SortingDropdown).prop('orderField')).toEqual(expectedField); - expect(wrapper.find(SortingDropdown).prop('orderDir')).toEqual(expectedDir); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({ field, dir }); }); }); diff --git a/test/tags/TagsList.test.tsx b/test/tags/TagsList.test.tsx index 86cf429e..4c5848b6 100644 --- a/test/tags/TagsList.test.tsx +++ b/test/tags/TagsList.test.tsx @@ -89,14 +89,11 @@ describe('', () => { it('triggers ordering when sorting dropdown changes', () => { const wrapper = createWrapper({ filteredTags: [] }); - expect(wrapper.find(SortingDropdown).prop('orderField')).not.toBeDefined(); - expect(wrapper.find(SortingDropdown).prop('orderDir')).not.toBeDefined(); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({}); wrapper.find(SortingDropdown).simulate('change', 'tag', 'DESC'); - expect(wrapper.find(SortingDropdown).prop('orderField')).toEqual('tag'); - expect(wrapper.find(SortingDropdown).prop('orderDir')).toEqual('DESC'); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({ field: 'tag', dir: 'DESC' }); wrapper.find(SortingDropdown).simulate('change', 'visits', 'ASC'); - expect(wrapper.find(SortingDropdown).prop('orderField')).toEqual('visits'); - expect(wrapper.find(SortingDropdown).prop('orderDir')).toEqual('ASC'); + expect(wrapper.find(SortingDropdown).prop('order')).toEqual({ field: 'visits', dir: 'ASC' }); }); it('can update current order via orderByColumn from table component', () => { diff --git a/test/utils/SortingDropdown.test.tsx b/test/utils/SortingDropdown.test.tsx index 518b5f12..934bbfb7 100644 --- a/test/utils/SortingDropdown.test.tsx +++ b/test/utils/SortingDropdown.test.tsx @@ -14,7 +14,7 @@ describe('', () => { baz: 'Hello World', }; const createWrapper = (props: Partial = {}) => { - wrapper = shallow(); + wrapper = shallow(); return wrapper; }; @@ -34,7 +34,7 @@ describe('', () => { }); it('properly marks selected field as active with proper icon', () => { - const wrapper = createWrapper({ orderField: 'bar', orderDir: 'DESC' }); + const wrapper = createWrapper({ order: { field: 'bar', dir: 'DESC' } }); const activeItem = wrapper.find('DropdownItem[active=true]'); const activeItemIcon = activeItem.first().find(FontAwesomeIcon); @@ -55,7 +55,7 @@ describe('', () => { it('triggers change function when item is clicked and an order field was provided', () => { const onChange = jest.fn(); - const wrapper = createWrapper({ onChange, orderField: 'baz', orderDir: 'ASC' }); + const wrapper = createWrapper({ onChange, order: { field: 'baz', dir: 'ASC' } }); const firstItem = wrapper.find(DropdownItem).first(); firstItem.simulate('click'); @@ -66,7 +66,7 @@ describe('', () => { it('updates order dir when already selected item is clicked', () => { const onChange = jest.fn(); - const wrapper = createWrapper({ onChange, orderField: 'foo', orderDir: 'ASC' }); + const wrapper = createWrapper({ onChange, order: { field: 'foo', dir: 'ASC' } }); const firstItem = wrapper.find(DropdownItem).first(); firstItem.simulate('click'); @@ -79,14 +79,14 @@ describe('', () => { [{ isButton: false }, <>Order by ], [{ isButton: true }, <>Order by... ], [ - { isButton: true, orderField: 'foo', orderDir: 'ASC' as OrderDir }, + { isButton: true, order: { field: 'foo', dir: 'ASC' as OrderDir } }, 'Order by: "Foo" - "ASC"', ], [ - { isButton: true, orderField: 'baz', orderDir: 'DESC' as OrderDir }, + { isButton: true, order: { field: 'baz', dir: 'DESC' as OrderDir } }, 'Order by: "Hello World" - "DESC"', ], - [{ isButton: true, orderField: 'baz' }, 'Order by: "Hello World" - "DESC"' ], + [{ isButton: true, order: { field: 'baz' } }, 'Order by: "Hello World" - "DESC"' ], ])('displays expected text in toggle', (props, expectedText) => { const wrapper = createWrapper(props); const toggle = wrapper.find(DropdownToggle);