diff --git a/src/short-urls/ShortUrlsFilteringBar.tsx b/src/short-urls/ShortUrlsFilteringBar.tsx index 5f87009a..a2725c2d 100644 --- a/src/short-urls/ShortUrlsFilteringBar.tsx +++ b/src/short-urls/ShortUrlsFilteringBar.tsx @@ -85,7 +85,12 @@ const ShortUrlsFilteringBar = (
- +
diff --git a/src/utils/OrderingDropdown.tsx b/src/utils/OrderingDropdown.tsx index 7b391c6a..6b458086 100644 --- a/src/utils/OrderingDropdown.tsx +++ b/src/utils/OrderingDropdown.tsx @@ -12,10 +12,11 @@ export interface OrderingDropdownProps { onChange: (orderField?: T, orderDir?: OrderDir) => void; isButton?: boolean; right?: boolean; + prefixed?: boolean; } export function OrderingDropdown( - { items, order, onChange, isButton = true, right = false }: OrderingDropdownProps, + { items, order, onChange, isButton = true, right = false, prefixed = true }: OrderingDropdownProps, ) { const handleItemClick = (fieldKey: T) => () => { const newOrderDir = determineOrderDir(fieldKey, order.field, order.dir); @@ -28,11 +29,14 @@ export function OrderingDropdown( {!isButton && <>Order by} - {isButton && !order.field && <>Order by...} - {isButton && order.field && `Order by: "${items[order.field]}" - "${order.dir ?? 'DESC'}"`} + {isButton && !order.field && Order by...} + {isButton && order.field && <>{prefixed && 'Order by: '}{items[order.field]} - {order.dir ?? 'DESC'}} ({ @@ -17,6 +16,8 @@ jest.mock('react-router-dom', () => ({ useLocation: jest.fn().mockReturnValue({}), })); +const TooltipToggleSwitch = () => null; // TODO Drop this! + describe('', () => { let wrapper: ShallowWrapper; const ExportShortUrlsBtn = () => null; diff --git a/test/utils/OrderingDropdown.test.tsx b/test/utils/OrderingDropdown.test.tsx new file mode 100644 index 00000000..3c978e1a --- /dev/null +++ b/test/utils/OrderingDropdown.test.tsx @@ -0,0 +1,108 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { values } from 'ramda'; +import { OrderingDropdown, OrderingDropdownProps } from '../../src/utils/OrderingDropdown'; +import { OrderDir } from '../../src/utils/helpers/ordering'; + +describe('', () => { + const items = { + foo: 'Foo', + bar: 'Bar', + baz: 'Hello World', + }; + const setUp = (props: Partial = {}) => ({ + user: userEvent.setup(), + ...render(), + }); + const setUpWithDisplayedMenu = async (props: Partial = {}) => { + const result = setUp(props); + const { user } = result; + + await user.click(screen.getByRole('button')); + expect(await screen.findByRole('menu')).toBeInTheDocument(); + + return result; + }; + + it('properly renders provided list of items', async () => { + await setUpWithDisplayedMenu(); + + const dropdownItems = screen.getAllByRole('menuitem'); + + expect(dropdownItems).toHaveLength(values(items).length); + expect(dropdownItems[0]).toHaveTextContent('Foo'); + expect(dropdownItems[1]).toHaveTextContent('Bar'); + expect(dropdownItems[2]).toHaveTextContent('Hello World'); + expect(screen.getByRole('button', { name: 'Clear selection' })).toBeInTheDocument(); + }); + + it.each([ + ['foo', 0], + ['bar', 1], + ['baz', 2], + ])('properly marks selected field as active with proper icon', async (field, expectedActiveIndex) => { + await setUpWithDisplayedMenu({ order: { field, dir: 'DESC' } }); + + const dropdownItems = screen.getAllByRole('menuitem'); + + expect(dropdownItems).toHaveLength(4); + expect(screen.queryByRole('button', { name: 'Clear selection' })).not.toBeInTheDocument(); + + dropdownItems.forEach((item, index) => { + if (index === expectedActiveIndex) { + expect(item).toHaveAttribute('class', expect.stringContaining('active')); + } else { + expect(item).not.toHaveAttribute('class', expect.stringContaining('active')); + } + }); + }); + + it.each([ + [{} as any, 'foo', 'ASC'], + [{ field: 'baz', dir: 'ASC' } as any, 'foo', 'ASC'], + [{ field: 'foo', dir: 'ASC' } as any, 'foo', 'DESC'], + [{ field: 'foo', dir: 'DESC' } as any, undefined, undefined], + ])( + 'triggers change with proper params depending on clicked item and initial state', + async (initialOrder, expectedNewField, expectedNewDir) => { + const onChange = jest.fn(); + const { user } = await setUpWithDisplayedMenu({ onChange, order: initialOrder }); + + await user.click(screen.getAllByRole('menuitem')[0]); + + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith(expectedNewField, expectedNewDir); + }, + ); + + it('clears selection when last item is clicked', async () => { + const onChange = jest.fn(); + const { user } = await setUpWithDisplayedMenu({ onChange, order: { field: 'baz', dir: 'ASC' } }); + + await user.click(screen.getAllByRole('menuitem')[3]); + + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith(); + }); + + it.each([ + [{ isButton: false }, /Order by$/], + [{ isButton: true }, 'Order by...'], + [ + { isButton: true, order: { field: 'foo', dir: 'ASC' as OrderDir } }, + 'Order by: Foo - ASC', + ], + [ + { isButton: true, order: { field: 'baz', dir: 'DESC' as OrderDir } }, + 'Order by: Hello World - DESC', + ], + [{ isButton: true, order: { field: 'baz' } }, 'Order by: Hello World - DESC'], + [ + { isButton: true, order: { field: 'baz', dir: 'DESC' as OrderDir }, prefixed: false }, + /^Hello World - DESC/, + ], + ])('with %s props displays %s in toggle', async (props, expectedText) => { + setUp(props); + expect(screen.getByRole('button')).toHaveTextContent(expectedText); + }); +}); diff --git a/test/utils/SortingDropdown.test.tsx b/test/utils/SortingDropdown.test.tsx deleted file mode 100644 index aff01c1a..00000000 --- a/test/utils/SortingDropdown.test.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { shallow, ShallowWrapper } from 'enzyme'; -import { DropdownItem, DropdownToggle } from 'reactstrap'; -import { identity, values } from 'ramda'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faSortAmountDown as caretDownIcon } from '@fortawesome/free-solid-svg-icons'; -import { OrderingDropdown, OrderingDropdownProps } from '../../src/utils/OrderingDropdown'; -import { OrderDir } from '../../src/utils/helpers/ordering'; - -describe('', () => { - let wrapper: ShallowWrapper; - const items = { - foo: 'Foo', - bar: 'Bar', - baz: 'Hello World', - }; - const createWrapper = (props: Partial = {}) => { - wrapper = shallow(); - - return wrapper; - }; - - afterEach(() => wrapper?.unmount()); - - it('properly renders provided list of items', () => { - const wrapper = createWrapper(); - const dropdownItems = wrapper.find(DropdownItem); - const secondIndex = 2; - const clearItemsCount = 2; - - expect(dropdownItems).toHaveLength(values(items).length + clearItemsCount); - expect(dropdownItems.at(0).html()).toContain('Foo'); - expect(dropdownItems.at(1).html()).toContain('Bar'); - expect(dropdownItems.at(secondIndex).html()).toContain('Hello World'); - }); - - it('properly marks selected field as active with proper icon', () => { - const wrapper = createWrapper({ order: { field: 'bar', dir: 'DESC' } }); - const activeItem = wrapper.find('DropdownItem[active=true]'); - const activeItemIcon = activeItem.first().find(FontAwesomeIcon); - - expect(activeItem).toHaveLength(1); - expect(activeItemIcon.prop('icon')).toEqual(caretDownIcon); - }); - - it('triggers change function when item is clicked and no order field was provided', () => { - const onChange = jest.fn(); - const wrapper = createWrapper({ onChange }); - const firstItem = wrapper.find(DropdownItem).first(); - - firstItem.simulate('click'); - - expect(onChange).toHaveBeenCalledTimes(1); - expect(onChange).toHaveBeenCalledWith('foo', 'ASC'); - }); - - it('triggers change function when item is clicked and an order field was provided', () => { - const onChange = jest.fn(); - const wrapper = createWrapper({ onChange, order: { field: 'baz', dir: 'ASC' } }); - const firstItem = wrapper.find(DropdownItem).first(); - - firstItem.simulate('click'); - - expect(onChange).toHaveBeenCalledTimes(1); - expect(onChange).toHaveBeenCalledWith('foo', 'ASC'); - }); - - it('updates order dir when already selected item is clicked', () => { - const onChange = jest.fn(); - const wrapper = createWrapper({ onChange, order: { field: 'foo', dir: 'ASC' } }); - const firstItem = wrapper.find(DropdownItem).first(); - - firstItem.simulate('click'); - - expect(onChange).toHaveBeenCalledTimes(1); - expect(onChange).toHaveBeenCalledWith('foo', 'DESC'); - }); - - it.each([ - [{ isButton: false }, <>Order by], - [{ isButton: true }, <>Order by...], - [ - { isButton: true, order: { field: 'foo', dir: 'ASC' as OrderDir } }, - 'Order by: "Foo" - "ASC"', - ], - [ - { isButton: true, order: { field: 'baz', dir: 'DESC' as OrderDir } }, - '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); - const [children] = (toggle.prop('children') as any[]).filter(Boolean); - - expect(children).toEqual(expectedText); - }); -});