diff --git a/src/settings/Tags.tsx b/src/settings/Tags.tsx index 47d52260..63bc4aab 100644 --- a/src/settings/Tags.tsx +++ b/src/settings/Tags.tsx @@ -3,6 +3,8 @@ import { FormGroup } from 'reactstrap'; import { SimpleCard } from '../utils/SimpleCard'; import { TagsModeDropdown } from '../tags/TagsModeDropdown'; import { capitalize } from '../utils/utils'; +import SortingDropdown from '../utils/SortingDropdown'; +import { SORTABLE_FIELDS } from '../tags/data/TagsListChildrenProps'; import { Settings, TagsSettings } from './reducers/settings'; interface TagsProps { @@ -12,7 +14,7 @@ interface TagsProps { export const Tags: FC = ({ settings: { tags }, setTagsSettings }) => ( - + = ({ settings: { tags }, setTagsSettings }) => /> Tags will be displayed as {tags?.defaultMode ?? 'cards'}. + + + setTagsSettings({ ...tags, defaultOrdering: { field, dir } })} + /> + ); diff --git a/src/settings/reducers/settings.ts b/src/settings/reducers/settings.ts index ee88cdc4..2a7e0fcc 100644 --- a/src/settings/reducers/settings.ts +++ b/src/settings/reducers/settings.ts @@ -5,6 +5,7 @@ import { RecursivePartial } from '../../utils/utils'; import { Theme } from '../../utils/theme'; import { DateInterval } from '../../utils/dates/types'; import { TagsOrder } from '../../tags/data/TagsListChildrenProps'; +import { ShortUrlsOrder } from '../../short-urls/reducers/shortUrlsListParams'; export const SET_SETTINGS = 'shlink/realTimeUpdates/SET_SETTINGS'; @@ -41,9 +42,14 @@ export interface TagsSettings { defaultMode?: TagsMode; } +export interface ShortUrlListSettings { + defaultOrdering?: ShortUrlsOrder; +} + export interface Settings { realTimeUpdates: RealTimeUpdatesSettings; shortUrlCreation?: ShortUrlCreationSettings; + shortUrlList?: ShortUrlListSettings; ui?: UiSettings; visits?: VisitsSettings; tags?: TagsSettings; diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index b5fd5250..f7198a44 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -3,14 +3,14 @@ import { FC, useEffect, useMemo, useState } from 'react'; import { RouteComponentProps } from 'react-router'; import { Card } from 'reactstrap'; import SortingDropdown from '../utils/SortingDropdown'; -import { determineOrderDir, Order, OrderDir } from '../utils/helpers/ordering'; +import { determineOrderDir, OrderDir } from '../utils/helpers/ordering'; import { getServerId, SelectedServer } from '../servers/data'; import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub'; import { Topics } from '../mercure/helpers/Topics'; import { TableOrderIcon } from '../utils/table/TableOrderIcon'; import { ShlinkShortUrlsListParams } from '../api/types'; import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList'; -import { OrderableFields, ShortUrlsListParams, SORTABLE_FIELDS } from './reducers/shortUrlsListParams'; +import { OrderableFields, ShortUrlsListParams, ShortUrlsOrder, SORTABLE_FIELDS } from './reducers/shortUrlsListParams'; import { ShortUrlsTableProps } from './ShortUrlsTable'; import Paginator from './Paginator'; import { ShortUrlListRouteParams, useShortUrlsQuery } from './helpers/hooks'; @@ -23,8 +23,6 @@ interface ShortUrlsListProps extends RouteComponentProps void; } -type ShortUrlsOrder = Order; - const ShortUrlsList = (ShortUrlsTable: FC, SearchBar: FC) => boundToMercureHub(({ listShortUrls, resetShortUrlParams, diff --git a/src/short-urls/reducers/shortUrlsListParams.ts b/src/short-urls/reducers/shortUrlsListParams.ts index c05d0ccc..cefc4814 100644 --- a/src/short-urls/reducers/shortUrlsListParams.ts +++ b/src/short-urls/reducers/shortUrlsListParams.ts @@ -1,5 +1,5 @@ import { buildActionCreator, buildReducer } from '../../utils/helpers/redux'; -import { OrderDir } from '../../utils/helpers/ordering'; +import { Order, OrderDir } from '../../utils/helpers/ordering'; import { LIST_SHORT_URLS, ListShortUrlsAction } from './shortUrlsList'; export const RESET_SHORT_URL_PARAMS = 'shlink/shortUrlsListParams/RESET_SHORT_URL_PARAMS'; @@ -14,6 +14,8 @@ export const SORTABLE_FIELDS = { export type OrderableFields = keyof typeof SORTABLE_FIELDS; +export type ShortUrlsOrder = Order; + export type OrderBy = Partial>; export interface ShortUrlsListParams { diff --git a/src/tags/TagsList.tsx b/src/tags/TagsList.tsx index 7aff73af..63832b53 100644 --- a/src/tags/TagsList.tsx +++ b/src/tags/TagsList.tsx @@ -29,7 +29,7 @@ const TagsList = (TagsCards: FC, TagsTable: FC { const [ mode, setMode ] = useState(settings.tags?.defaultMode ?? 'cards'); - const [ order, setOrder ] = useState({}); + const [ order, setOrder ] = useState(settings.tags?.defaultOrdering ?? {}); const resolveSortedTags = pipe( () => tagsList.filteredTags.map((tag): NormalizedTag => ({ tag, diff --git a/test/settings/Tags.test.tsx b/test/settings/Tags.test.tsx index b9084fc9..8e5440a4 100644 --- a/test/settings/Tags.test.tsx +++ b/test/settings/Tags.test.tsx @@ -1,8 +1,11 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; +import { FormGroup } from 'reactstrap'; import { Settings, TagsMode, TagsSettings } from '../../src/settings/reducers/settings'; import { TagsModeDropdown } from '../../src/tags/TagsModeDropdown'; import { Tags } from '../../src/settings/Tags'; +import SortingDropdown from '../../src/utils/SortingDropdown'; +import { TagsOrder } from '../../src/tags/data/TagsListChildrenProps'; describe('', () => { let wrapper: ShallowWrapper; @@ -16,6 +19,13 @@ describe('', () => { afterEach(() => wrapper?.unmount()); afterEach(jest.clearAllMocks); + it('renders expected amount of groups', () => { + const wrapper = createWrapper(); + const groups = wrapper.find(FormGroup); + + expect(groups).toHaveLength(2); + }); + it.each([ [ undefined, 'cards' ], [{}, 'cards' ], @@ -41,4 +51,31 @@ describe('', () => { dropdown.simulate('change', defaultMode); expect(setTagsSettings).toHaveBeenCalledWith({ defaultMode }); }); + + it.each([ + [ undefined, {}], + [{}, {}], + [{ defaultOrdering: {} }, {}], + [{ defaultOrdering: { field: 'tag', dir: 'DESC' } as TagsOrder }, { field: 'tag', dir: 'DESC' }], + [{ defaultOrdering: { field: 'visits', dir: 'ASC' } as TagsOrder }, { field: 'visits', dir: 'ASC' }], + ])('shows expected ordering', (tags, expectedOrder) => { + const wrapper = createWrapper(tags); + const dropdown = wrapper.find(SortingDropdown); + + expect(dropdown.prop('order')).toEqual(expectedOrder); + }); + + it.each([ + [ undefined, undefined ], + [ 'tag', 'ASC' ], + [ 'visits', undefined ], + [ 'shortUrls', 'DESC' ], + ])('invokes setTagsSettings when ordering changes', (field, dir) => { + const wrapper = createWrapper(); + const dropdown = wrapper.find(SortingDropdown); + + expect(setTagsSettings).not.toHaveBeenCalled(); + dropdown.simulate('change', field, dir); + expect(setTagsSettings).toHaveBeenCalledWith({ defaultOrdering: { field, dir } }); + }); });