mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-09 17:57:26 +03:00
Moved table sorting icon to its own component wrapping the logic
This commit is contained in:
parent
5906921eec
commit
fe81e023e8
5 changed files with 35 additions and 27 deletions
|
@ -1,5 +1,3 @@
|
||||||
import { faCaretDown as caretDownIcon, faCaretUp as caretUpIcon } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import { head, keys, values } from 'ramda';
|
import { head, keys, values } from 'ramda';
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { RouteComponentProps } from 'react-router';
|
import { RouteComponentProps } from 'react-router';
|
||||||
|
@ -10,6 +8,7 @@ import { getServerId, SelectedServer } from '../servers/data';
|
||||||
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
|
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
|
||||||
import { parseQuery } from '../utils/helpers/query';
|
import { parseQuery } from '../utils/helpers/query';
|
||||||
import { Topics } from '../mercure/helpers/Topics';
|
import { Topics } from '../mercure/helpers/Topics';
|
||||||
|
import { TableOrderIcon } from '../utils/table/TableOrderIcon';
|
||||||
import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList';
|
import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList';
|
||||||
import { OrderableFields, ShortUrlsListParams, SORTABLE_FIELDS } from './reducers/shortUrlsListParams';
|
import { OrderableFields, ShortUrlsListParams, SORTABLE_FIELDS } from './reducers/shortUrlsListParams';
|
||||||
import { ShortUrlsTableProps } from './ShortUrlsTable';
|
import { ShortUrlsTableProps } from './ShortUrlsTable';
|
||||||
|
@ -52,8 +51,7 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
|
||||||
};
|
};
|
||||||
const orderByColumn = (field: OrderableFields) => () =>
|
const orderByColumn = (field: OrderableFields) => () =>
|
||||||
handleOrderBy(field, determineOrderDir(field, order.field, order.dir));
|
handleOrderBy(field, determineOrderDir(field, order.field, order.dir));
|
||||||
const renderOrderIcon = (field: OrderableFields) => order.dir && order.field === field &&
|
const renderOrderIcon = (field: OrderableFields) => <TableOrderIcon currentOrder={order} field={field} />;
|
||||||
<FontAwesomeIcon icon={order.dir === 'ASC' ? caretUpIcon : caretDownIcon} className="ml-1" />;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { tag } = parseQuery<{ tag?: string }>(location.search);
|
const { tag } = parseQuery<{ tag?: string }>(location.search);
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { FC, useEffect, useRef } from 'react';
|
import { FC, useEffect, useRef } from 'react';
|
||||||
import { splitEvery } from 'ramda';
|
import { splitEvery } from 'ramda';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import { faCaretDown as caretDownIcon, faCaretUp as caretUpIcon } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { RouteChildrenProps } from 'react-router';
|
import { RouteChildrenProps } from 'react-router';
|
||||||
import { SimpleCard } from '../utils/SimpleCard';
|
import { SimpleCard } from '../utils/SimpleCard';
|
||||||
import SimplePaginator from '../common/SimplePaginator';
|
import SimplePaginator from '../common/SimplePaginator';
|
||||||
import { useQueryState } from '../utils/helpers/hooks';
|
import { useQueryState } from '../utils/helpers/hooks';
|
||||||
import { parseQuery } from '../utils/helpers/query';
|
import { parseQuery } from '../utils/helpers/query';
|
||||||
|
import { TableOrderIcon } from '../utils/table/TableOrderIcon';
|
||||||
import { OrderableFields, TagsListChildrenProps, TagsOrder } from './data/TagsListChildrenProps';
|
import { OrderableFields, TagsListChildrenProps, TagsOrder } from './data/TagsListChildrenProps';
|
||||||
import { TagsTableRowProps } from './TagsTableRow';
|
import { TagsTableRowProps } from './TagsTableRow';
|
||||||
import './TagsTable.scss';
|
import './TagsTable.scss';
|
||||||
|
@ -27,8 +26,6 @@ export const TagsTable = (TagsTableRow: FC<TagsTableRowProps>) => (
|
||||||
const pages = splitEvery(TAGS_PER_PAGE, sortedTags);
|
const pages = splitEvery(TAGS_PER_PAGE, sortedTags);
|
||||||
const showPaginator = pages.length > 1;
|
const showPaginator = pages.length > 1;
|
||||||
const currentPage = pages[page - 1] ?? [];
|
const currentPage = pages[page - 1] ?? [];
|
||||||
const renderOrderIcon = (field: OrderableFields) => currentOrder.dir && currentOrder.field === field &&
|
|
||||||
<FontAwesomeIcon icon={currentOrder.dir === 'ASC' ? caretUpIcon : caretDownIcon} className="ml-1" />;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
!isFirstLoad.current && setPage(1);
|
!isFirstLoad.current && setPage(1);
|
||||||
|
@ -43,12 +40,14 @@ export const TagsTable = (TagsTableRow: FC<TagsTableRowProps>) => (
|
||||||
<table className="table table-hover mb-0">
|
<table className="table table-hover mb-0">
|
||||||
<thead className="responsive-table__header">
|
<thead className="responsive-table__header">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="tags-table__header-cell" onClick={orderByColumn('tag')}>Tag {renderOrderIcon('tag')}</th>
|
<th className="tags-table__header-cell" onClick={orderByColumn('tag')}>
|
||||||
|
Tag <TableOrderIcon currentOrder={currentOrder} field="tag" />
|
||||||
|
</th>
|
||||||
<th className="tags-table__header-cell text-lg-right" onClick={orderByColumn('shortUrls')}>
|
<th className="tags-table__header-cell text-lg-right" onClick={orderByColumn('shortUrls')}>
|
||||||
Short URLs {renderOrderIcon('shortUrls')}
|
Short URLs <TableOrderIcon currentOrder={currentOrder} field="shortUrls" />
|
||||||
</th>
|
</th>
|
||||||
<th className="tags-table__header-cell text-lg-right" onClick={orderByColumn('visits')}>
|
<th className="tags-table__header-cell text-lg-right" onClick={orderByColumn('visits')}>
|
||||||
Visits {renderOrderIcon('visits')}
|
Visits <TableOrderIcon currentOrder={currentOrder} field="visits" />
|
||||||
</th>
|
</th>
|
||||||
<th className="tags-table__header-cell" />
|
<th className="tags-table__header-cell" />
|
||||||
</tr>
|
</tr>
|
||||||
|
|
19
src/utils/table/TableOrderIcon.tsx
Normal file
19
src/utils/table/TableOrderIcon.tsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { faCaretDown as caretDownIcon, faCaretUp as caretUpIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { Order } from '../helpers/ordering';
|
||||||
|
|
||||||
|
interface TableOrderIconProps<T> {
|
||||||
|
currentOrder: Order<T>;
|
||||||
|
field: T;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TableOrderIcon<T extends string = string>(
|
||||||
|
{ currentOrder, field, className = 'ml-1' }: TableOrderIconProps<T>,
|
||||||
|
) {
|
||||||
|
if (!currentOrder.dir || currentOrder.field !== field) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <FontAwesomeIcon icon={currentOrder.dir === 'ASC' ? caretUpIcon : caretDownIcon} className={className} />;
|
||||||
|
}
|
|
@ -1,12 +1,7 @@
|
||||||
import { useEffect, useMemo, useState, useRef } from 'react';
|
import { useEffect, useMemo, useState, useRef } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { min, splitEvery } from 'ramda';
|
import { min, splitEvery } from 'ramda';
|
||||||
import {
|
import { faCheck as checkIcon, faRobot as botIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
faCaretDown as caretDownIcon,
|
|
||||||
faCaretUp as caretUpIcon,
|
|
||||||
faCheck as checkIcon,
|
|
||||||
faRobot as botIcon,
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { UncontrolledTooltip } from 'reactstrap';
|
import { UncontrolledTooltip } from 'reactstrap';
|
||||||
import SimplePaginator from '../common/SimplePaginator';
|
import SimplePaginator from '../common/SimplePaginator';
|
||||||
|
@ -16,6 +11,7 @@ import { prettify } from '../utils/helpers/numbers';
|
||||||
import { supportsBotVisits } from '../utils/helpers/features';
|
import { supportsBotVisits } from '../utils/helpers/features';
|
||||||
import { SelectedServer } from '../servers/data';
|
import { SelectedServer } from '../servers/data';
|
||||||
import { Time } from '../utils/Time';
|
import { Time } from '../utils/Time';
|
||||||
|
import { TableOrderIcon } from '../utils/table/TableOrderIcon';
|
||||||
import { NormalizedOrphanVisit, NormalizedVisit } from './types';
|
import { NormalizedOrphanVisit, NormalizedVisit } from './types';
|
||||||
import './VisitsTable.scss';
|
import './VisitsTable.scss';
|
||||||
|
|
||||||
|
@ -72,12 +68,8 @@ const VisitsTable = ({
|
||||||
|
|
||||||
const orderByColumn = (field: OrderableFields) =>
|
const orderByColumn = (field: OrderableFields) =>
|
||||||
() => setOrder({ field, dir: determineOrderDir(field, order.field, order.dir) });
|
() => setOrder({ field, dir: determineOrderDir(field, order.field, order.dir) });
|
||||||
const renderOrderIcon = (field: OrderableFields) => order.dir && order.field === field && (
|
const renderOrderIcon = (field: OrderableFields) =>
|
||||||
<FontAwesomeIcon
|
<TableOrderIcon currentOrder={order} field={field} className="visits-table__header-icon" />;
|
||||||
icon={order.dir === 'ASC' ? caretUpIcon : caretDownIcon}
|
|
||||||
className="visits-table__header-icon"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listener = () => setIsMobileDevice(matchMobile());
|
const listener = () => setIsMobileDevice(matchMobile());
|
||||||
|
|
|
@ -77,15 +77,15 @@ describe('<ShortUrlsList />', () => {
|
||||||
|
|
||||||
it('invokes order icon rendering', () => {
|
it('invokes order icon rendering', () => {
|
||||||
const renderIcon = (field: OrderableFields) =>
|
const renderIcon = (field: OrderableFields) =>
|
||||||
(wrapper.find(ShortUrlsTable).prop('renderOrderIcon') as (field: OrderableFields) => ReactElement | null)(field);
|
(wrapper.find(ShortUrlsTable).prop('renderOrderIcon') as (field: OrderableFields) => ReactElement)(field);
|
||||||
|
|
||||||
expect(renderIcon('visits')).toEqual(undefined);
|
expect(renderIcon('visits').props.currentOrder).toEqual({});
|
||||||
|
|
||||||
wrapper.find(SortingDropdown).simulate('change', 'visits');
|
wrapper.find(SortingDropdown).simulate('change', 'visits');
|
||||||
expect(renderIcon('visits')).toEqual(undefined);
|
expect(renderIcon('visits').props.currentOrder).toEqual({ field: 'visits' });
|
||||||
|
|
||||||
wrapper.find(SortingDropdown).simulate('change', 'visits', 'ASC');
|
wrapper.find(SortingDropdown).simulate('change', 'visits', 'ASC');
|
||||||
expect(renderIcon('visits')).not.toEqual(undefined);
|
expect(renderIcon('visits').props.currentOrder).toEqual({ field: 'visits', dir: 'ASC' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles order by through table', () => {
|
it('handles order by through table', () => {
|
||||||
|
|
Loading…
Reference in a new issue