From 21b8e05e354619d226b6746681e03f6dfd0e6b0a Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 10 Nov 2021 22:25:56 +0100 Subject: [PATCH] Moved dates handling in short URLs list to query --- src/short-urls/SearchBar.tsx | 15 ++++++-------- src/short-urls/ShortUrlsList.tsx | 12 ++++++----- src/short-urls/helpers/hooks.ts | 2 ++ src/short-urls/services/provideServices.ts | 2 +- src/utils/dates/DateRangeSelector.tsx | 18 ++++++----------- test/short-urls/SearchBar.test.tsx | 23 ++++++++++++++-------- 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/short-urls/SearchBar.tsx b/src/short-urls/SearchBar.tsx index 0590a183..3397c55c 100644 --- a/src/short-urls/SearchBar.tsx +++ b/src/short-urls/SearchBar.tsx @@ -14,23 +14,20 @@ import { ShortUrlListRouteParams, useShortUrlsQuery } from './helpers/hooks'; import './SearchBar.scss'; export interface SearchBarProps extends RouteChildrenProps { - listShortUrls: (params: ShortUrlsListParams) => void; shortUrlsListParams: ShortUrlsListParams; } const dateOrNull = (date?: string) => date ? parseISO(date) : null; -const SearchBar = (colorGenerator: ColorGenerator) => ( - { listShortUrls, shortUrlsListParams, ...rest }: SearchBarProps, -) => { - const [{ search, tags }, toFirstPage ] = useShortUrlsQuery(rest); - const selectedTags = tags?.split(',').map(decodeURIComponent) ?? []; +const SearchBar = (colorGenerator: ColorGenerator) => ({ shortUrlsListParams, ...rest }: SearchBarProps) => { + const [{ search, tags, startDate, endDate }, toFirstPage ] = useShortUrlsQuery(rest); + const selectedTags = tags?.split(',') ?? []; const setDates = pipe( ({ startDate, endDate }: DateRange) => ({ startDate: formatIsoDate(startDate) ?? undefined, endDate: formatIsoDate(endDate) ?? undefined, }), - (dates) => listShortUrls({ ...shortUrlsListParams, ...dates }), + toFirstPage, ); const setSearch = pipe( (searchTerm: string) => isEmpty(searchTerm) ? undefined : searchTerm, @@ -52,8 +49,8 @@ const SearchBar = (colorGenerator: ColorGenerator) => ( diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index 1a303cbd..1fc22f1e 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -40,8 +40,8 @@ const ShortUrlsList = (ShortUrlsTable: FC, SearchBar: FC) = field: orderBy && (head(keys(orderBy)) as OrderableFields), dir: orderBy && head(values(orderBy)), }); - const [{ tags, search }, toFirstPage ] = useShortUrlsQuery({ history, match, location }); - const decodedTags = useMemo(() => tags?.split(',').map(decodeURIComponent) ?? [], [ tags ]); + const [{ tags, search, startDate, endDate }, toFirstPage ] = useShortUrlsQuery({ history, match, location }); + const selectedTags = useMemo(() => tags?.split(',') ?? [], [ tags ]); const { pagination } = shortUrlsList?.shortUrls ?? {}; const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams }); @@ -53,14 +53,16 @@ const ShortUrlsList = (ShortUrlsTable: FC, SearchBar: FC) = handleOrderBy(field, determineOrderDir(field, order.field, order.dir)); const renderOrderIcon = (field: OrderableFields) => ; const addTag = pipe( - (newTag: string) => [ ...new Set([ ...decodedTags, newTag ]) ].join(','), + (newTag: string) => [ ...new Set([ ...selectedTags, newTag ]) ].join(','), (tags) => toFirstPage({ tags }), ); useEffect(() => resetShortUrlParams, []); useEffect(() => { - refreshList({ page: match.params.page, searchTerm: search, tags: decodedTags, itemsPerPage: undefined }); - }, [ match.params.page, search, decodedTags ]); + refreshList( + { page: match.params.page, searchTerm: search, tags: selectedTags, itemsPerPage: undefined, startDate, endDate }, + ); + }, [ match.params.page, search, selectedTags, startDate, endDate ]); return ( <> diff --git a/src/short-urls/helpers/hooks.ts b/src/short-urls/helpers/hooks.ts index e7ff9f78..0cbbca1f 100644 --- a/src/short-urls/helpers/hooks.ts +++ b/src/short-urls/helpers/hooks.ts @@ -14,6 +14,8 @@ export interface ShortUrlListRouteParams { interface ShortUrlsQuery { tags?: string; search?: string; + startDate?: string; + endDate?: string; } export const useShortUrlsQuery = ({ history, location, match }: ServerIdRouteProps): [ShortUrlsQuery, ToFirstPage] => { diff --git a/src/short-urls/services/provideServices.ts b/src/short-urls/services/provideServices.ts index 197c6941..6f1b70dc 100644 --- a/src/short-urls/services/provideServices.ts +++ b/src/short-urls/services/provideServices.ts @@ -52,7 +52,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter: // Services bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator'); - bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ])); + bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ])); bottle.decorator('SearchBar', withRouter); // Actions diff --git a/src/utils/dates/DateRangeSelector.tsx b/src/utils/dates/DateRangeSelector.tsx index 1b1ec95c..b0a3c104 100644 --- a/src/utils/dates/DateRangeSelector.tsx +++ b/src/utils/dates/DateRangeSelector.tsx @@ -22,18 +22,16 @@ export interface DateRangeSelectorProps { export const DateRangeSelector = ( { onDatesChange, initialDateRange, defaultText, disabled }: DateRangeSelectorProps, ) => { - const [ activeInterval, setActiveInterval ] = useState( - rangeIsInterval(initialDateRange) ? initialDateRange : undefined, - ); - const [ activeDateRange, setActiveDateRange ] = useState( - !rangeIsInterval(initialDateRange) ? initialDateRange : undefined, - ); + const initialIntervalIsRange = rangeIsInterval(initialDateRange); + const [ activeInterval, setActiveInterval ] = useState(initialIntervalIsRange ? initialDateRange : undefined); + const [ activeDateRange, setActiveDateRange ] = useState(initialIntervalIsRange ? undefined : initialDateRange); + const updateDateRange = (dateRange: DateRange) => { setActiveInterval(dateRangeIsEmpty(dateRange) ? 'all' : undefined); setActiveDateRange(dateRange); onDatesChange(dateRange); }; - const updateInterval = (dateInterval: DateInterval) => () => { + const updateInterval = (dateInterval: DateInterval) => { setActiveInterval(dateInterval); setActiveDateRange(undefined); onDatesChange(intervalToDateRange(dateInterval)); @@ -41,11 +39,7 @@ export const DateRangeSelector = ( return ( - updateInterval(interval)()} - /> + Custom: diff --git a/test/short-urls/SearchBar.test.tsx b/test/short-urls/SearchBar.test.tsx index cc609369..f698f665 100644 --- a/test/short-urls/SearchBar.test.tsx +++ b/test/short-urls/SearchBar.test.tsx @@ -2,6 +2,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; import { History, Location } from 'history'; import { match } from 'react-router'; +import { formatISO } from 'date-fns'; import searchBarCreator, { SearchBarProps } from '../../src/short-urls/SearchBar'; import SearchField from '../../src/utils/SearchField'; import Tag from '../../src/tags/helpers/Tag'; @@ -11,13 +12,12 @@ import { ShortUrlListRouteParams } from '../../src/short-urls/helpers/hooks'; describe('', () => { let wrapper: ShallowWrapper; - const listShortUrlsMock = jest.fn(); const SearchBar = searchBarCreator(Mock.all()); const push = jest.fn(); + const now = new Date(); const createWrapper = (props: Partial = {}) => { wrapper = shallow( ({ push })} location={Mock.of({ search: '' })} @@ -50,7 +50,7 @@ describe('', () => { expect(wrapper.find(Tag)).toHaveLength(expectedTagComps); }); - it('updates short URLs list when search field changes', () => { + it('redirects to first page when search field changes', () => { const wrapper = createWrapper(); const searchField = wrapper.find(SearchField); @@ -59,7 +59,7 @@ describe('', () => { expect(push).toHaveBeenCalledWith('/server/1/list-short-urls/1?search=search-term'); }); - it('updates short URLs list when a tag is removed', () => { + it('redirects to first page when a tag is removed', () => { const wrapper = createWrapper({ location: Mock.of({ search: 'tags=foo,bar' }) }); const tag = wrapper.find(Tag).first(); @@ -68,12 +68,19 @@ describe('', () => { expect(push).toHaveBeenCalledWith('/server/1/list-short-urls/1?tags=bar'); }); - it('updates short URLs list when date range changes', () => { + it.each([ + [{ startDate: now }, `startDate=${encodeURIComponent(formatISO(now))}` ], + [{ endDate: now }, `endDate=${encodeURIComponent(formatISO(now))}` ], + [ + { startDate: now, endDate: now }, + `startDate=${encodeURIComponent(formatISO(now))}&endDate=${encodeURIComponent(formatISO(now))}`, + ], + ])('redirects to first page when date range changes', (dates, expectedQuery) => { const wrapper = createWrapper(); const dateRange = wrapper.find(DateRangeSelector); - expect(listShortUrlsMock).not.toHaveBeenCalled(); - dateRange.simulate('datesChange', {}); - expect(listShortUrlsMock).toHaveBeenCalledTimes(1); + expect(push).not.toHaveBeenCalled(); + dateRange.simulate('datesChange', dates); + expect(push).toHaveBeenCalledWith(`/server/1/list-short-urls/1?${expectedQuery}`); }); });