Merge pull request #369 from acelaya-forks/feature/consistent-dropdowns

Feature/consistent dropdowns
This commit is contained in:
Alejandro Celaya 2020-12-25 11:25:48 +01:00 committed by GitHub
commit 3bf64bee1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 196 additions and 141 deletions

View file

@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
## [Unreleased]
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#364](https://github.com/shlinkio/shlink-web-client/issues/364) Fixed all dropdowns so that they are consistently styled.
## [3.0.0] - 2020-12-22 ## [3.0.0] - 2020-12-22
### Added ### Added
* [#340](https://github.com/shlinkio/shlink-web-client/issues/340) Added new "overview" page, showing basic information of the active server. * [#340](https://github.com/shlinkio/shlink-web-client/issues/340) Added new "overview" page, showing basic information of the active server.

View file

@ -1,31 +1,12 @@
@import '../utils/mixins/vertical-align'; @import '../utils/mixins/vertical-align';
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn,
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn:hover,
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn:active {
text-align: left;
color: #6c757d;
border-color: #ced4da;
background-color: white;
}
.domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active, .domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active,
.domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active:hover, .domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active:hover,
.domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active:active { .domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active:active {
color: #495057; color: #495057 !important;
} }
.domains-dropdown__back-btn.domains-dropdown__back-btn, .domains-dropdown__back-btn.domains-dropdown__back-btn,
.domains-dropdown__back-btn.domains-dropdown__back-btn:hover { .domains-dropdown__back-btn.domains-dropdown__back-btn:hover {
border-color: #ced4da; border-color: #ced4da;
} }
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn::after {
right: .75rem;
@include vertical-align();
}
.domains-dropdown__menu {
width: 100%;
}

View file

@ -1,20 +1,10 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { import { Button, DropdownItem, Input, InputGroup, InputGroupAddon, UncontrolledTooltip } from 'reactstrap';
Button,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownToggle,
Input,
InputGroup,
InputGroupAddon,
UncontrolledTooltip,
} from 'reactstrap';
import { InputProps } from 'reactstrap/lib/Input'; import { InputProps } from 'reactstrap/lib/Input';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUndo } from '@fortawesome/free-solid-svg-icons'; import { faUndo } from '@fortawesome/free-solid-svg-icons';
import { isEmpty, pipe } from 'ramda'; import { isEmpty, pipe } from 'ramda';
import classNames from 'classnames'; import { DropdownBtn } from '../utils/DropdownBtn';
import { useToggle } from '../utils/helpers/hooks'; import { useToggle } from '../utils/helpers/hooks';
import { DomainsList } from './reducers/domainsList'; import { DomainsList } from './reducers/domainsList';
import './DomainSelector.scss'; import './DomainSelector.scss';
@ -31,7 +21,6 @@ interface DomainSelectorConnectProps extends DomainSelectorProps {
export const DomainSelector = ({ listDomains, value, domainsList, onChange }: DomainSelectorConnectProps) => { export const DomainSelector = ({ listDomains, value, domainsList, onChange }: DomainSelectorConnectProps) => {
const [ inputDisplayed,, showInput, hideInput ] = useToggle(); const [ inputDisplayed,, showInput, hideInput ] = useToggle();
const [ isDropdownOpen, toggleDropdown ] = useToggle();
const { domains } = domainsList; const { domains } = domainsList;
const valueIsEmpty = isEmpty(value); const valueIsEmpty = isEmpty(value);
const unselectDomain = () => onChange(''); const unselectDomain = () => onChange('');
@ -63,33 +52,24 @@ export const DomainSelector = ({ listDomains, value, domainsList, onChange }: Do
</InputGroupAddon> </InputGroupAddon>
</InputGroup> </InputGroup>
) : ( ) : (
<Dropdown isOpen={isDropdownOpen} toggle={toggleDropdown}> <DropdownBtn
<DropdownToggle text={valueIsEmpty ? 'Domain' : `Domain: ${value}`}
caret className={!valueIsEmpty ? 'domains-dropdown__toggle-btn--active' : ''}
className={classNames( >
'domains-dropdown__toggle-btn btn-block', {domains.map(({ domain, isDefault }) => (
{ 'domains-dropdown__toggle-btn--active': !valueIsEmpty }, <DropdownItem
)} key={domain}
> active={value === domain || isDefault && valueIsEmpty}
{valueIsEmpty && <>Domain</>} onClick={() => onChange(domain)}
{!valueIsEmpty && <>Domain: {value}</>} >
</DropdownToggle> {domain}
<DropdownMenu className="domains-dropdown__menu"> {isDefault && <span className="float-right text-muted">default</span>}
{domains.map(({ domain, isDefault }) => (
<DropdownItem
key={domain}
active={value === domain || isDefault && valueIsEmpty}
onClick={() => onChange(domain)}
>
{domain}
{isDefault && <span className="float-right text-muted">default</span>}
</DropdownItem>
))}
<DropdownItem divider />
<DropdownItem onClick={pipe(unselectDomain, showInput)}>
<i>New domain</i>
</DropdownItem> </DropdownItem>
</DropdownMenu> ))}
</Dropdown> <DropdownItem divider />
<DropdownItem onClick={pipe(unselectDomain, showInput)}>
<i>New domain</i>
</DropdownItem>
</DropdownBtn>
); );
}; };

View file

@ -1,12 +1,9 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { Card } from 'reactstrap';
import Paginator from './Paginator';
import { ShortUrlsListProps } from './ShortUrlsList'; import { ShortUrlsListProps } from './ShortUrlsList';
const ShortUrls = (SearchBar: FC, ShortUrlsList: FC<ShortUrlsListProps>) => (props: ShortUrlsListProps) => { const ShortUrls = (SearchBar: FC, ShortUrlsList: FC<ShortUrlsListProps>) => (props: ShortUrlsListProps) => {
const { match, shortUrlsList } = props; const { match } = props;
const { page = '1', serverId = '' } = match?.params ?? {}; const { page = '1', serverId = '' } = match?.params ?? {};
const { pagination } = shortUrlsList?.shortUrls ?? {};
const [ urlsListKey, setUrlsListKey ] = useState(`${serverId}_${page}`); const [ urlsListKey, setUrlsListKey ] = useState(`${serverId}_${page}`);
// Using a key on a component makes react to create a new instance every time the key changes // Using a key on a component makes react to create a new instance every time the key changes
@ -18,10 +15,7 @@ const ShortUrls = (SearchBar: FC, ShortUrlsList: FC<ShortUrlsListProps>) => (pro
return ( return (
<> <>
<div className="form-group"><SearchBar /></div> <div className="form-group"><SearchBar /></div>
<Card body className="pb-1"> <ShortUrlsList {...props} key={urlsListKey} />
<ShortUrlsList {...props} key={urlsListKey} />
<Paginator paginator={pagination} serverId={serverId} />
</Card>
</> </>
); );
}; };

View file

@ -3,14 +3,16 @@ 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';
import { Card } from 'reactstrap';
import SortingDropdown from '../utils/SortingDropdown'; import SortingDropdown from '../utils/SortingDropdown';
import { determineOrderDir, OrderDir } from '../utils/utils'; import { determineOrderDir, OrderDir } from '../utils/utils';
import { SelectedServer } from '../servers/data'; import { isReachableServer, 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 { 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';
import Paginator from './Paginator';
import './ShortUrlsList.scss'; import './ShortUrlsList.scss';
interface RouteParams { interface RouteParams {
@ -40,6 +42,7 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
orderField: orderBy && (head(keys(orderBy)) as OrderableFields), orderField: orderBy && (head(keys(orderBy)) as OrderableFields),
orderDir: orderBy && head(values(orderBy)), orderDir: orderBy && head(values(orderBy)),
}); });
const { pagination } = shortUrlsList?.shortUrls ?? {};
const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams }); const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams });
const handleOrderBy = (orderField?: OrderableFields, orderDir?: OrderDir) => { const handleOrderBy = (orderField?: OrderableFields, orderDir?: OrderDir) => {
setOrder({ orderField, orderDir }); setOrder({ orderField, orderDir });
@ -83,13 +86,16 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
onChange={handleOrderBy} onChange={handleOrderBy}
/> />
</div> </div>
<ShortUrlsTable <Card body className="pb-1">
orderByColumn={orderByColumn} <ShortUrlsTable
renderOrderIcon={renderOrderIcon} orderByColumn={orderByColumn}
selectedServer={selectedServer} renderOrderIcon={renderOrderIcon}
shortUrlsList={shortUrlsList} selectedServer={selectedServer}
onTagClick={(tag) => refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })} shortUrlsList={shortUrlsList}
/> onTagClick={(tag) => refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })}
/>
<Paginator paginator={pagination} serverId={isReachableServer(selectedServer) ? selectedServer.id : ''} />
</Card>
</> </>
); );
}, () => 'https://shlink.io/new-visit'); }, () => 'https://shlink.io/new-visit');

View file

@ -0,0 +1,19 @@
@import '../utils/mixins/vertical-align';
.dropdown-btn__toggle.dropdown-btn__toggle,
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled).active,
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):active,
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):focus,
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):hover,
.show > .dropdown-btn__toggle.dropdown-btn__toggle.dropdown-toggle {
color: #6c757d;
background-color: white;
text-align: left;
border-color: rgba(0, 0, 0, .125);
}
.dropdown-btn__toggle.dropdown-btn__toggle:after {
@include vertical-align();
right: .75rem;
}

22
src/utils/DropdownBtn.tsx Normal file
View file

@ -0,0 +1,22 @@
import { FC } from 'react';
import { Dropdown, DropdownMenu, DropdownToggle } from 'reactstrap';
import { useToggle } from './helpers/hooks';
import './DropdownBtn.scss';
export interface DropdownBtnProps {
text: string;
disabled?: boolean;
className?: string;
}
export const DropdownBtn: FC<DropdownBtnProps> = ({ text, disabled = false, className = '', children }) => {
const [ isOpen, toggle ] = useToggle();
const toggleClasses = `dropdown-btn__toggle btn-block ${className}`;
return (
<Dropdown isOpen={isOpen} toggle={toggle} disabled={disabled}>
<DropdownToggle caret className={toggleClasses} color="primary">{text}</DropdownToggle>
<DropdownMenu className="w-100">{children}</DropdownMenu>
</Dropdown>
);
};

View file

@ -28,10 +28,12 @@ export default function SortingDropdown<T extends string = string>(
<UncontrolledDropdown> <UncontrolledDropdown>
<DropdownToggle <DropdownToggle
caret caret
color={isButton ? 'secondary' : 'link'} color={isButton ? 'primary' : 'link'}
className={classNames({ 'btn-block': isButton, 'btn-sm p-0': !isButton })} className={classNames({ 'dropdown-btn__toggle btn-block': isButton, 'btn-sm p-0': !isButton })}
> >
Order by {!isButton && <>Order by</>}
{isButton && !orderField && <>Order by...</>}
{isButton && orderField && `Order by: "${items[orderField]}" - "${orderDir ?? 'DESC'}"`}
</DropdownToggle> </DropdownToggle>
<DropdownMenu <DropdownMenu
right={right} right={right}

View file

@ -1,19 +0,0 @@
@import '../../utils/mixins/vertical-align';
.date-range-selector__btn.date-range-selector__btn,
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled).active,
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled):active,
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled):focus,
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled):hover,
.show > .date-range-selector__btn.date-range-selector__btn.dropdown-toggle {
color: #6c757d;
background-color: white;
text-align: left;
border-color: rgba(0, 0, 0, .125);
}
.date-range-selector__btn.date-range-selector__btn:after {
@include vertical-align();
right: .75rem;
}

View file

@ -1,6 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap'; import { DropdownItem } from 'reactstrap';
import { useToggle } from '../helpers/hooks'; import { DropdownBtn } from '../DropdownBtn';
import { import {
DateInterval, DateInterval,
DateRange, DateRange,
@ -10,7 +10,6 @@ import {
rangeIsInterval, rangeIsInterval,
} from './types'; } from './types';
import DateRangeRow from './DateRangeRow'; import DateRangeRow from './DateRangeRow';
import './DateRangeSelector.scss';
export interface DateRangeSelectorProps { export interface DateRangeSelectorProps {
initialDateRange?: DateInterval | DateRange; initialDateRange?: DateInterval | DateRange;
@ -20,9 +19,8 @@ export interface DateRangeSelectorProps {
} }
export const DateRangeSelector = ( export const DateRangeSelector = (
{ onDatesChange, initialDateRange, defaultText, disabled = false }: DateRangeSelectorProps, { onDatesChange, initialDateRange, defaultText, disabled }: DateRangeSelectorProps,
) => { ) => {
const [ isOpen, toggle ] = useToggle();
const [ activeInterval, setActiveInterval ] = useState( const [ activeInterval, setActiveInterval ] = useState(
rangeIsInterval(initialDateRange) ? initialDateRange : undefined, rangeIsInterval(initialDateRange) ? initialDateRange : undefined,
); );
@ -41,35 +39,30 @@ export const DateRangeSelector = (
}; };
return ( return (
<Dropdown isOpen={isOpen} toggle={toggle} disabled={disabled}> <DropdownBtn disabled={disabled} text={rangeOrIntervalToString(activeInterval ?? activeDateRange) ?? defaultText}>
<DropdownToggle caret className="date-range-selector__btn btn-block" color="primary"> <DropdownItem
{rangeOrIntervalToString(activeInterval ?? activeDateRange) ?? defaultText} active={activeInterval === undefined && dateRangeIsEmpty(activeDateRange)}
</DropdownToggle> onClick={updateInterval(undefined)}
<DropdownMenu className="w-100"> >
<DropdownItem {defaultText}
active={activeInterval === undefined && dateRangeIsEmpty(activeDateRange)} </DropdownItem>
onClick={updateInterval(undefined)} <DropdownItem divider />
> {([ 'today', 'yesterday', 'last7Days', 'last30Days', 'last90Days', 'last180days', 'last365Days' ] as DateInterval[]).map(
{defaultText} (interval) => (
</DropdownItem> <DropdownItem key={interval} active={activeInterval === interval} onClick={updateInterval(interval)}>
<DropdownItem divider /> {rangeOrIntervalToString(interval)}
{([ 'today', 'yesterday', 'last7Days', 'last30Days', 'last90Days', 'last180days', 'last365Days' ] as DateInterval[]).map( </DropdownItem>
(interval) => ( ),
<DropdownItem key={interval} active={activeInterval === interval} onClick={updateInterval(interval)}> )}
{rangeOrIntervalToString(interval)} <DropdownItem divider />
</DropdownItem> <DropdownItem header>Custom:</DropdownItem>
), <DropdownItem text>
)} <DateRangeRow
<DropdownItem divider /> {...activeDateRange}
<DropdownItem header>Custom:</DropdownItem> onStartDateChange={(startDate) => updateDateRange({ ...activeDateRange, startDate })}
<DropdownItem text> onEndDateChange={(endDate) => updateDateRange({ ...activeDateRange, endDate })}
<DateRangeRow />
{...activeDateRange} </DropdownItem>
onStartDateChange={(startDate) => updateDateRange({ ...activeDateRange, startDate })} </DropdownBtn>
onEndDateChange={(endDate) => updateDateRange({ ...activeDateRange, endDate })}
/>
</DropdownItem>
</DropdownMenu>
</Dropdown>
); );
}; };

View file

@ -1,9 +1,10 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { shallow, ShallowWrapper } from 'enzyme';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { DropdownItem, DropdownMenu, InputGroup } from 'reactstrap'; import { DropdownItem, InputGroup } from 'reactstrap';
import { DomainSelector } from '../../src/domains/DomainSelector'; import { DomainSelector } from '../../src/domains/DomainSelector';
import { DomainsList } from '../../src/domains/reducers/domainsList'; import { DomainsList } from '../../src/domains/reducers/domainsList';
import { ShlinkDomain } from '../../src/api/types'; import { ShlinkDomain } from '../../src/api/types';
import { DropdownBtn } from '../../src/utils/DropdownBtn';
describe('<DomainSelector />', () => { describe('<DomainSelector />', () => {
let wrapper: ShallowWrapper; let wrapper: ShallowWrapper;
@ -23,7 +24,7 @@ describe('<DomainSelector />', () => {
it('shows dropdown by default', () => { it('shows dropdown by default', () => {
const input = wrapper.find(InputGroup); const input = wrapper.find(InputGroup);
const dropdown = wrapper.find(DropdownMenu); const dropdown = wrapper.find(DropdownBtn);
expect(input).toHaveLength(0); expect(input).toHaveLength(0);
expect(dropdown).toHaveLength(1); expect(dropdown).toHaveLength(1);
@ -33,10 +34,10 @@ describe('<DomainSelector />', () => {
it('allows to toggle between dropdown and input', () => { it('allows to toggle between dropdown and input', () => {
wrapper.find(DropdownItem).last().simulate('click'); wrapper.find(DropdownItem).last().simulate('click');
expect(wrapper.find(InputGroup)).toHaveLength(1); expect(wrapper.find(InputGroup)).toHaveLength(1);
expect(wrapper.find(DropdownMenu)).toHaveLength(0); expect(wrapper.find(DropdownBtn)).toHaveLength(0);
wrapper.find('.domains-dropdown__back-btn').simulate('click'); wrapper.find('.domains-dropdown__back-btn').simulate('click');
expect(wrapper.find(InputGroup)).toHaveLength(0); expect(wrapper.find(InputGroup)).toHaveLength(0);
expect(wrapper.find(DropdownMenu)).toHaveLength(1); expect(wrapper.find(DropdownBtn)).toHaveLength(1);
}); });
}); });

View file

@ -1,7 +1,6 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { shallow, ShallowWrapper } from 'enzyme';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import shortUrlsCreator from '../../src/short-urls/ShortUrls'; import shortUrlsCreator from '../../src/short-urls/ShortUrls';
import Paginator from '../../src/short-urls/Paginator';
import { ShortUrlsListProps } from '../../src/short-urls/ShortUrlsList'; import { ShortUrlsListProps } from '../../src/short-urls/ShortUrlsList';
describe('<ShortUrls />', () => { describe('<ShortUrls />', () => {
@ -18,9 +17,8 @@ describe('<ShortUrls />', () => {
}); });
afterEach(() => wrapper.unmount()); afterEach(() => wrapper.unmount());
it('wraps a SearchBar, ShortUrlsList as Paginator', () => { it('wraps a SearchBar and ShortUrlsList', () => {
expect(wrapper.find(SearchBar)).toHaveLength(1); expect(wrapper.find(SearchBar)).toHaveLength(1);
expect(wrapper.find(ShortUrlsList)).toHaveLength(1); expect(wrapper.find(ShortUrlsList)).toHaveLength(1);
expect(wrapper.find(Paginator)).toHaveLength(1);
}); });
}); });

View file

@ -0,0 +1,41 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { DropdownMenu, DropdownToggle } from 'reactstrap';
import { PropsWithChildren } from 'react';
import { DropdownBtn, DropdownBtnProps } from '../../src/utils/DropdownBtn';
describe('<DropdownBtn />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (props: PropsWithChildren<DropdownBtnProps>) => {
wrapper = shallow(<DropdownBtn {...props} />);
return wrapper;
};
afterEach(() => wrapper?.unmount());
it.each([[ 'foo' ], [ 'bar' ], [ 'baz' ]])('displays provided text', (text) => {
const wrapper = createWrapper({ text });
const toggle = wrapper.find(DropdownToggle);
expect(toggle.html()).toContain(text);
});
it.each([[ 'foo' ], [ 'bar' ], [ 'baz' ]])('displays provided children', (children) => {
const wrapper = createWrapper({ text: '', children });
const menu = wrapper.find(DropdownMenu);
expect(menu.html()).toContain(children);
});
it.each([
[ undefined, 'dropdown-btn__toggle btn-block' ],
[ '', 'dropdown-btn__toggle btn-block' ],
[ 'foo', 'dropdown-btn__toggle btn-block foo' ],
[ 'bar', 'dropdown-btn__toggle btn-block bar' ],
])('includes provided classes', (className, expectedClasses) => {
const wrapper = createWrapper({ text: '', className });
const toggle = wrapper.find(DropdownToggle);
expect(toggle.prop('className')?.trim()).toEqual(expectedClasses);
});
});

View file

@ -1,9 +1,10 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { shallow, ShallowWrapper } from 'enzyme';
import { DropdownItem } from 'reactstrap'; import { DropdownItem, DropdownToggle } from 'reactstrap';
import { identity, values } from 'ramda'; import { identity, values } from 'ramda';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSortAmountDown as caretDownIcon } from '@fortawesome/free-solid-svg-icons'; import { faSortAmountDown as caretDownIcon } from '@fortawesome/free-solid-svg-icons';
import SortingDropdown, { SortingDropdownProps } from '../../src/utils/SortingDropdown'; import SortingDropdown, { SortingDropdownProps } from '../../src/utils/SortingDropdown';
import { OrderDir } from '../../src/utils/utils';
describe('<SortingDropdown />', () => { describe('<SortingDropdown />', () => {
let wrapper: ShallowWrapper; let wrapper: ShallowWrapper;
@ -73,4 +74,23 @@ describe('<SortingDropdown />', () => {
expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('foo', 'DESC'); expect(onChange).toHaveBeenCalledWith('foo', 'DESC');
}); });
it.each([
[{ isButton: false }, '>Order by<' ],
[{ isButton: true }, '>Order by...<' ],
[
{ isButton: true, orderField: 'foo', orderDir: 'ASC' as OrderDir },
'Order by: &quot;Foo&quot; - &quot;ASC&quot;',
],
[
{ isButton: true, orderField: 'baz', orderDir: 'DESC' as OrderDir },
'Order by: &quot;Hello World&quot; - &quot;DESC&quot;',
],
[{ isButton: true, orderField: 'baz' }, 'Order by: &quot;Hello World&quot; - &quot;DESC&quot;' ],
])('displays expected text in toggle', (props, expectedText) => {
const wrapper = createWrapper(props);
const toggle = wrapper.find(DropdownToggle);
expect(toggle.html()).toContain(expectedText);
});
}); });