Repository: shlinkio/shlink-web-client (ts)

Issue:
Adapt domains dropdown to have the same style as the date range selector
<!--
Before opening an issue, just take into account that this is a completely free of charge and open source project.
I'm always happy to help and provide support, but some understanding will be expected.
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
You may also be asked to provide tests or ways to reproduce reported bugs.
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
-->


Pull request description:
Feature/consistent dropdowns
Closes #364 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f84c1824..4ef4cceb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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).
 
+## [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
 ### Added
 * [#340](https://github.com/shlinkio/shlink-web-client/issues/340) Added new "overview" page, showing basic information of the active server.
diff --git a/src/domains/DomainSelector.scss b/src/domains/DomainSelector.scss
index c9ab0ba0..89e02433 100644
--- a/src/domains/DomainSelector.scss
+++ b/src/domains/DomainSelector.scss
@@ -1,31 +1,12 @@
 @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:hover,
 .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:hover {
   border-color: #ced4da;
 }
-
-.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn::after {
-  right: .75rem;
-
-  @include vertical-align();
-}
-
-.domains-dropdown__menu {
-  width: 100%;
-}
diff --git a/src/domains/DomainSelector.tsx b/src/domains/DomainSelector.tsx
index 510b39f9..c843f409 100644
--- a/src/domains/DomainSelector.tsx
+++ b/src/domains/DomainSelector.tsx
@@ -1,20 +1,10 @@
 import { useEffect } from 'react';
-import {
-  Button,
-  Dropdown,
-  DropdownItem,
-  DropdownMenu,
-  DropdownToggle,
-  Input,
-  InputGroup,
-  InputGroupAddon,
-  UncontrolledTooltip,
-} from 'reactstrap';
+import { Button, DropdownItem, Input, InputGroup, InputGroupAddon, UncontrolledTooltip } from 'reactstrap';
 import { InputProps } from 'reactstrap/lib/Input';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faUndo } from '@fortawesome/free-solid-svg-icons';
 import { isEmpty, pipe } from 'ramda';
-import classNames from 'classnames';
+import { DropdownBtn } from '../utils/DropdownBtn';
 import { useToggle } from '../utils/helpers/hooks';
 import { DomainsList } from './reducers/domainsList';
 import './DomainSelector.scss';
@@ -31,7 +21,6 @@ interface DomainSelectorConnectProps extends DomainSelectorProps {
 
 export const DomainSelector = ({ listDomains, value, domainsList, onChange }: DomainSelectorConnectProps) => {
   const [ inputDisplayed,, showInput, hideInput ] = useToggle();
-  const [ isDropdownOpen, toggleDropdown ] = useToggle();
   const { domains } = domainsList;
   const valueIsEmpty = isEmpty(value);
   const unselectDomain = () => onChange('');
@@ -63,33 +52,24 @@ export const DomainSelector = ({ listDomains, value, domainsList, onChange }: Do
       </InputGroupAddon>
     </InputGroup>
   ) : (
-    <Dropdown isOpen={isDropdownOpen} toggle={toggleDropdown}>
-      <DropdownToggle
-        caret
-        className={classNames(
-          'domains-dropdown__toggle-btn btn-block',
-          { 'domains-dropdown__toggle-btn--active': !valueIsEmpty },
-        )}
-      >
-        {valueIsEmpty && <>Domain</>}
-        {!valueIsEmpty && <>Domain: {value}</>}
-      </DropdownToggle>
-      <DropdownMenu className="domains-dropdown__menu">
-        {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>
+    <DropdownBtn
+      text={valueIsEmpty ? 'Domain' : `Domain: ${value}`}
+      className={!valueIsEmpty ? 'domains-dropdown__toggle-btn--active' : ''}
+    >
+      {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>
-      </DropdownMenu>
-    </Dropdown>
+      ))}
+      <DropdownItem divider />
+      <DropdownItem onClick={pipe(unselectDomain, showInput)}>
+        <i>New domain</i>
+      </DropdownItem>
+    </DropdownBtn>
   );
 };
diff --git a/src/short-urls/ShortUrls.tsx b/src/short-urls/ShortUrls.tsx
index d6dea6bc..13814837 100644
--- a/src/short-urls/ShortUrls.tsx
+++ b/src/short-urls/ShortUrls.tsx
@@ -1,12 +1,9 @@
 import { FC, useEffect, useState } from 'react';
-import { Card } from 'reactstrap';
-import Paginator from './Paginator';
 import { ShortUrlsListProps } from './ShortUrlsList';
 
 const ShortUrls = (SearchBar: FC, ShortUrlsList: FC<ShortUrlsListProps>) => (props: ShortUrlsListProps) => {
-  const { match, shortUrlsList } = props;
+  const { match } = props;
   const { page = '1', serverId = '' } = match?.params ?? {};
-  const { pagination } = shortUrlsList?.shortUrls ?? {};
   const [ urlsListKey, setUrlsListKey ] = useState(`${serverId}_${page}`);
 
   // 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 (
     <>
       <div className="form-group"><SearchBar /></div>
-      <Card body className="pb-1">
-        <ShortUrlsList {...props} key={urlsListKey} />
-        <Paginator paginator={pagination} serverId={serverId} />
-      </Card>
+      <ShortUrlsList {...props} key={urlsListKey} />
     </>
   );
 };
diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx
index 86397f73..c92ec4c1 100644
--- a/src/short-urls/ShortUrlsList.tsx
+++ b/src/short-urls/ShortUrlsList.tsx
@@ -3,14 +3,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { head, keys, values } from 'ramda';
 import { FC, useEffect, useState } from 'react';
 import { RouteComponentProps } from 'react-router';
+import { Card } from 'reactstrap';
 import SortingDropdown from '../utils/SortingDropdown';
 import { determineOrderDir, OrderDir } from '../utils/utils';
-import { SelectedServer } from '../servers/data';
+import { isReachableServer, SelectedServer } from '../servers/data';
 import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
 import { parseQuery } from '../utils/helpers/query';
 import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList';
 import { OrderableFields, ShortUrlsListParams, SORTABLE_FIELDS } from './reducers/shortUrlsListParams';
 import { ShortUrlsTableProps } from './ShortUrlsTable';
+import Paginator from './Paginator';
 import './ShortUrlsList.scss';
 
 interface RouteParams {
@@ -40,6 +42,7 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
     orderField: orderBy && (head(keys(orderBy)) as OrderableFields),
     orderDir: orderBy && head(values(orderBy)),
   });
+  const { pagination } = shortUrlsList?.shortUrls ?? {};
   const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams });
   const handleOrderBy = (orderField?: OrderableFields, orderDir?: OrderDir) => {
     setOrder({ orderField, orderDir });
@@ -83,13 +86,16 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
           onChange={handleOrderBy}
         />
       </div>
-      <ShortUrlsTable
-        orderByColumn={orderByColumn}
-        renderOrderIcon={renderOrderIcon}
-        selectedServer={selectedServer}
-        shortUrlsList={shortUrlsList}
-        onTagClick={(tag) => refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })}
-      />
+      <Card body className="pb-1">
+        <ShortUrlsTable
+          orderByColumn={orderByColumn}
+          renderOrderIcon={renderOrderIcon}
+          selectedServer={selectedServer}
+          shortUrlsList={shortUrlsList}
+          onTagClick={(tag) => refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })}
+        />
+        <Paginator paginator={pagination} serverId={isReachableServer(selectedServer) ? selectedServer.id : ''} />
+      </Card>
     </>
   );
 }, () => 'https://shlink.io/new-visit');
diff --git a/src/utils/DropdownBtn.scss b/src/utils/DropdownBtn.scss
new file mode 100644
index 00000000..6cfae1dc
--- /dev/null
+++ b/src/utils/DropdownBtn.scss
@@ -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;
+}
diff --git a/src/utils/DropdownBtn.tsx b/src/utils/DropdownBtn.tsx
new file mode 100644
index 00000000..d1cf6f6b
--- /dev/null
+++ b/src/utils/DropdownBtn.tsx
@@ -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>
+  );
+};
diff --git a/src/utils/SortingDropdown.tsx b/src/utils/SortingDropdown.tsx
index 76fd1ca4..d9e06ec3 100644
--- a/src/utils/SortingDropdown.tsx
+++ b/src/utils/SortingDropdown.tsx
@@ -28,10 +28,12 @@ export default function SortingDropdown<T extends string = string>(
     <UncontrolledDropdown>
       <DropdownToggle
         caret
-        color={isButton ? 'secondary' : 'link'}
-        className={classNames({ 'btn-block': isButton, 'btn-sm p-0': !isButton })}
+        color={isButton ? 'primary' : 'link'}
+        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>
       <DropdownMenu
         right={right}
diff --git a/src/utils/dates/DateRangeSelector.scss b/src/utils/dates/DateRangeSelector.scss
deleted file mode 100644
index 114c2b12..00000000
--- a/src/utils/dates/DateRangeSelector.scss
+++ /dev/null
@@ -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;
-}
diff --git a/src/utils/dates/DateRangeSelector.tsx b/src/utils/dates/DateRangeSelector.tsx
index 7fbdc7bf..1b680f90 100644
--- a/src/utils/dates/DateRangeSelector.tsx
+++ b/src/utils/dates/DateRangeSelector.tsx
@@ -1,6 +1,6 @@
 import { useState } from 'react';
-import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
-import { useToggle } from '../helpers/hooks';
+import { DropdownItem } from 'reactstrap';
+import { DropdownBtn } from '../DropdownBtn';
 import {
   DateInterval,
   DateRange,
@@ -10,7 +10,6 @@ import {
   rangeIsInterval,
 } from './types';
 import DateRangeRow from './DateRangeRow';
-import './DateRangeSelector.scss';
 
 export interface DateRangeSelectorProps {
   initialDateRange?: DateInterval | DateRange;
@@ -20,9 +19,8 @@ export interface DateRangeSelectorProps {
 }
 
 export const DateRangeSelector = (
-  { onDatesChange, initialDateRange, defaultText, disabled = false }: DateRangeSelectorProps,
+  { onDatesChange, initialDateRange, defaultText, disabled }: DateRangeSelectorProps,
 ) => {
-  const [ isOpen, toggle ] = useToggle();
   const [ activeInterval, setActiveInterval ] = useState(
     rangeIsInterval(initialDateRange) ? initialDateRange : undefined,
   );
@@ -41,35 +39,30 @@ export const DateRangeSelector = (
   };
 
   return (
-    <Dropdown isOpen={isOpen} toggle={toggle} disabled={disabled}>
-      <DropdownToggle caret className="date-range-selector__btn btn-block" color="primary">
-        {rangeOrIntervalToString(activeInterval ?? activeDateRange) ?? defaultText}
-      </DropdownToggle>
-      <DropdownMenu className="w-100">
-        <DropdownItem
-          active={activeInterval === undefined && dateRangeIsEmpty(activeDateRange)}
-          onClick={updateInterval(undefined)}
-        >
-          {defaultText}
-        </DropdownItem>
-        <DropdownItem divider />
-        {([ 'today', 'yesterday', 'last7Days', 'last30Days', 'last90Days', 'last180days', 'last365Days' ] as DateInterval[]).map(
-          (interval) => (
-            <DropdownItem key={interval} active={activeInterval === interval} onClick={updateInterval(interval)}>
-              {rangeOrIntervalToString(interval)}
-            </DropdownItem>
-          ),
-        )}
-        <DropdownItem divider />
-        <DropdownItem header>Custom:</DropdownItem>
-        <DropdownItem text>
-          <DateRangeRow
-            {...activeDateRange}
-            onStartDateChange={(startDate) => updateDateRange({ ...activeDateRange, startDate })}
-            onEndDateChange={(endDate) => updateDateRange({ ...activeDateRange, endDate })}
-          />
-        </DropdownItem>
-      </DropdownMenu>
-    </Dropdown>
+    <DropdownBtn disabled={disabled} text={rangeOrIntervalToString(activeInterval ?? activeDateRange) ?? defaultText}>
+      <DropdownItem
+        active={activeInterval === undefined && dateRangeIsEmpty(activeDateRange)}
+        onClick={updateInterval(undefined)}
+      >
+        {defaultText}
+      </DropdownItem>
+      <DropdownItem divider />
+      {([ 'today', 'yesterday', 'last7Days', 'last30Days', 'last90Days', 'last180days', 'last365Days' ] as DateInterval[]).map(
+        (interval) => (
+          <DropdownItem key={interval} active={activeInterval === interval} onClick={updateInterval(interval)}>
+            {rangeOrIntervalToString(interval)}
+          </DropdownItem>
+        ),
+      )}
+      <DropdownItem divider />
+      <DropdownItem header>Custom:</DropdownItem>
+      <DropdownItem text>
+        <DateRangeRow
+          {...activeDateRange}
+          onStartDateChange={(startDate) => updateDateRange({ ...activeDateRange, startDate })}
+          onEndDateChange={(endDate) => updateDateRange({ ...activeDateRange, endDate })}
+        />
+      </DropdownItem>
+    </DropdownBtn>
   );
 };


Test patch:
diff --git a/test/domains/DomainSelector.test.tsx b/test/domains/DomainSelector.test.tsx
index 0c1ab22c..ec9ae9d6 100644
--- a/test/domains/DomainSelector.test.tsx
+++ b/test/domains/DomainSelector.test.tsx
@@ -1,9 +1,10 @@
 import { shallow, ShallowWrapper } from 'enzyme';
 import { Mock } from 'ts-mockery';
-import { DropdownItem, DropdownMenu, InputGroup } from 'reactstrap';
+import { DropdownItem, InputGroup } from 'reactstrap';
 import { DomainSelector } from '../../src/domains/DomainSelector';
 import { DomainsList } from '../../src/domains/reducers/domainsList';
 import { ShlinkDomain } from '../../src/api/types';
+import { DropdownBtn } from '../../src/utils/DropdownBtn';
 
 describe('<DomainSelector />', () => {
   let wrapper: ShallowWrapper;
@@ -23,7 +24,7 @@ describe('<DomainSelector />', () => {
 
   it('shows dropdown by default', () => {
     const input = wrapper.find(InputGroup);
-    const dropdown = wrapper.find(DropdownMenu);
+    const dropdown = wrapper.find(DropdownBtn);
 
     expect(input).toHaveLength(0);
     expect(dropdown).toHaveLength(1);
@@ -33,10 +34,10 @@ describe('<DomainSelector />', () => {
   it('allows to toggle between dropdown and input', () => {
     wrapper.find(DropdownItem).last().simulate('click');
     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');
     expect(wrapper.find(InputGroup)).toHaveLength(0);
-    expect(wrapper.find(DropdownMenu)).toHaveLength(1);
+    expect(wrapper.find(DropdownBtn)).toHaveLength(1);
   });
 });
diff --git a/test/short-urls/ShortUrls.test.tsx b/test/short-urls/ShortUrls.test.tsx
index c47ba0ce..f4caed88 100644
--- a/test/short-urls/ShortUrls.test.tsx
+++ b/test/short-urls/ShortUrls.test.tsx
@@ -1,7 +1,6 @@
 import { shallow, ShallowWrapper } from 'enzyme';
 import { Mock } from 'ts-mockery';
 import shortUrlsCreator from '../../src/short-urls/ShortUrls';
-import Paginator from '../../src/short-urls/Paginator';
 import { ShortUrlsListProps } from '../../src/short-urls/ShortUrlsList';
 
 describe('<ShortUrls />', () => {
@@ -18,9 +17,8 @@ describe('<ShortUrls />', () => {
   });
   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(ShortUrlsList)).toHaveLength(1);
-    expect(wrapper.find(Paginator)).toHaveLength(1);
   });
 });
diff --git a/test/utils/DropdownBtn.test.tsx b/test/utils/DropdownBtn.test.tsx
new file mode 100644
index 00000000..79722689
--- /dev/null
+++ b/test/utils/DropdownBtn.test.tsx
@@ -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);
+  });
+});
diff --git a/test/utils/SortingDropdown.test.tsx b/test/utils/SortingDropdown.test.tsx
index c327a172..6b3f9682 100644
--- a/test/utils/SortingDropdown.test.tsx
+++ b/test/utils/SortingDropdown.test.tsx
@@ -1,9 +1,10 @@
 import { shallow, ShallowWrapper } from 'enzyme';
-import { DropdownItem } from 'reactstrap';
+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 SortingDropdown, { SortingDropdownProps } from '../../src/utils/SortingDropdown';
+import { OrderDir } from '../../src/utils/utils';
 
 describe('<SortingDropdown />', () => {
   let wrapper: ShallowWrapper;
@@ -73,4 +74,23 @@ describe('<SortingDropdown />', () => {
     expect(onChange).toHaveBeenCalledTimes(1);
     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);
+  });
 });