Skip to content

Commit 89103b0

Browse files
BransonNgtaneliang
andauthored
Replace makeResponsive with useMediaQuery in 3 components (#3058)
Co-authored-by: E-Liang Tan <[email protected]>
1 parent 915d81a commit 89103b0

File tree

6 files changed

+263
-249
lines changed

6 files changed

+263
-249
lines changed

website/src/views/components/filters/DropdownListFilters.test.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import { mount } from 'enzyme';
22

33
import { RefinementItem } from 'types/views';
4-
import { DropdownListFiltersComponent } from './DropdownListFilters';
4+
import { mockDom, mockDomReset } from 'test-utils/mockDom';
5+
import DropdownListFilters from './DropdownListFilters';
6+
7+
describe(DropdownListFilters, () => {
8+
beforeEach(() => {
9+
mockDom();
10+
});
11+
12+
afterEach(() => {
13+
mockDomReset();
14+
});
515

6-
describe(DropdownListFiltersComponent, () => {
716
const CHECKBOX = '☑';
817

918
/* eslint-disable camelcase */
@@ -15,18 +24,17 @@ describe(DropdownListFiltersComponent, () => {
1524
];
1625
/* eslint-enable */
1726

18-
function make(items: RefinementItem[], selectedItems: string[], matchBreakpoint = false) {
27+
function make(items: RefinementItem[], selectedItems: string[]) {
1928
const onFilterChange = jest.fn();
2029

2130
return {
2231
onFilterChange,
2332

2433
wrapper: mount(
25-
<DropdownListFiltersComponent
34+
<DropdownListFilters
2635
items={items}
2736
selectedItems={selectedItems}
2837
toggleItem={onFilterChange}
29-
matchBreakpoint={matchBreakpoint}
3038
setItems={() => {
3139
/* do nothing; provide only because setItems must be set */
3240
}}
@@ -38,7 +46,7 @@ describe(DropdownListFiltersComponent, () => {
3846
// TODO: Write some sort of adaptor that reads off both <select> and <Downshift>
3947
// to ensure values in both match
4048
test('use native <select> element on mobile', () => {
41-
const { wrapper } = make(allItems, [], true);
49+
const { wrapper } = make(allItems, []);
4250

4351
expect(wrapper.find('select').exists()).toBe(true);
4452
expect(wrapper.find('option')).toHaveLength(allItems.length + 1); // 1 extra placeholder
@@ -47,7 +55,7 @@ describe(DropdownListFiltersComponent, () => {
4755
});
4856

4957
test('change value when <select> value changes', () => {
50-
const { wrapper, onFilterChange } = make(allItems, [], true);
58+
const { wrapper, onFilterChange } = make(allItems, []);
5159

5260
// Simulate selecting an <option> in the <select>
5361
const { key: firstItemKey } = allItems[0];
@@ -69,7 +77,7 @@ describe(DropdownListFiltersComponent, () => {
6977

7078
test('render a list of previously selected items outside the <select>', () => {
7179
const { key: firstItemKey } = allItems[0];
72-
const { wrapper, onFilterChange } = make(allItems, [firstItemKey], true);
80+
const { wrapper, onFilterChange } = make(allItems, [firstItemKey]);
7381

7482
// Should render the item in the checklist outside
7583
const checklist1 = wrapper.find('ul.list-unstyled input');

website/src/views/components/filters/DropdownListFilters.tsx

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import * as React from 'react';
2-
import { useState, useRef } from 'react';
1+
import { FC, useRef, useState } from 'react';
32
import Downshift, { DownshiftState, StateChangeOptions } from 'downshift';
43
import { ListProps } from 'searchkit';
54
import classnames from 'classnames';
65
import { uniq, omit } from 'lodash';
76

87
import { Search, ChevronDown } from 'react-feather';
9-
import makeResponsive, { WithBreakpoint } from 'views/hocs/makeResponsive';
8+
import useMediaQuery from 'views/hooks/useMediaQuery';
109
import { RefinementItem, RefinementDisplayItem } from 'types/views';
1110
import { highlight } from 'utils/react';
1211
import { breakpointDown, touchScreenOnly } from 'utils/css';
1312
import Checklist from './Checklist';
1413

1514
import styles from './styles.scss';
1615

17-
type Props = ListProps & WithBreakpoint;
16+
type Props = ListProps;
1817
type DisplayProps = {
1918
allItems: RefinementDisplayItem[];
2019
onSelectItem: (selectedItem: string) => void;
@@ -23,12 +22,7 @@ type DisplayProps = {
2322
};
2423

2524
// Use a native select for mobile devices
26-
const MobileFilter: React.FC<DisplayProps> = ({
27-
allItems,
28-
onSelectItem,
29-
showCount,
30-
placeholder,
31-
}) => (
25+
const MobileFilter: FC<DisplayProps> = ({ allItems, onSelectItem, showCount, placeholder }) => (
3226
<select
3327
className="form-control"
3428
onChange={(evt) => {
@@ -49,12 +43,7 @@ const MobileFilter: React.FC<DisplayProps> = ({
4943
);
5044

5145
// Use a search-select combo dropdown on desktop
52-
const DesktopFilter: React.FC<DisplayProps> = ({
53-
allItems,
54-
onSelectItem,
55-
showCount,
56-
placeholder,
57-
}) => {
46+
const DesktopFilter: FC<DisplayProps> = ({ allItems, onSelectItem, showCount, placeholder }) => {
5847
const [isFocused, setIsFocused] = useState(false);
5948
const [inputValue, setInputValue] = useState('');
6049
const searchInput = useRef<HTMLInputElement>(null);
@@ -167,13 +156,12 @@ const DesktopFilter: React.FC<DisplayProps> = ({
167156
);
168157
};
169158

170-
export const DropdownListFiltersComponent: React.FC<Props> = ({
159+
const DropdownListFilters: FC<Props> = ({
171160
items,
172161
selectedItems,
173162
toggleItem,
174163
showCount,
175164
translate,
176-
matchBreakpoint,
177165
}) => {
178166
const [searchedItems, setSearchedFilters] = useState(selectedItems);
179167

@@ -201,7 +189,9 @@ export const DropdownListFiltersComponent: React.FC<Props> = ({
201189
placeholder: translate ? translate('placeholder') : '',
202190
};
203191

204-
const FilterComponent = matchBreakpoint ? MobileFilter : DesktopFilter;
192+
const isMobile = useMediaQuery([breakpointDown('sm'), touchScreenOnly()]);
193+
194+
const FilterComponent = isMobile ? MobileFilter : DesktopFilter;
205195
return (
206196
<div className={styles.dropdown}>
207197
<FilterComponent {...displayProps} />
@@ -211,7 +201,4 @@ export const DropdownListFiltersComponent: React.FC<Props> = ({
211201
);
212202
};
213203

214-
export default makeResponsive(DropdownListFiltersComponent, [
215-
breakpointDown('sm'),
216-
touchScreenOnly(),
217-
]);
204+
export default DropdownListFilters;
Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { shallow } from 'enzyme';
22
import { noop } from 'lodash';
3+
import { mockWindowMatchMedia, mockDomReset } from 'test-utils/mockDom';
34
import ModuleFinderPagerButton from 'views/modules/ModuleFinderPagerButton';
4-
import { displayPageRange, ModuleFinderPagerComponent } from './ModuleFinderPager';
5+
import ModuleFinderPager, { displayPageRange } from './ModuleFinderPager';
56

67
describe(displayPageRange, () => {
78
test('calculate page range correctly', () => {
@@ -33,9 +34,17 @@ describe(displayPageRange, () => {
3334
});
3435
});
3536

36-
describe(ModuleFinderPagerComponent, () => {
37-
const DESKTOP = false;
38-
const MOBILE = true;
37+
const DESKTOP = false;
38+
const MOBILE = true;
39+
40+
describe('ModuleFinderPager', () => {
41+
beforeAll(() => {
42+
mockWindowMatchMedia();
43+
});
44+
45+
afterAll(() => {
46+
mockDomReset();
47+
});
3948

4049
const defaultProps = {
4150
selectedPage: 1,
@@ -45,66 +54,73 @@ describe(ModuleFinderPagerComponent, () => {
4554
onGoToPage: noop,
4655
onGoToNext: noop,
4756
onGoToLast: noop,
48-
matchBreakpoint: DESKTOP,
4957
};
5058

5159
test('should not render if totalNumPages <= 0', () => {
52-
const zeroPagesPager = shallow(
53-
<ModuleFinderPagerComponent {...defaultProps} totalNumPages={0} />,
54-
);
60+
const zeroPagesPager = shallow(<ModuleFinderPager {...defaultProps} totalNumPages={0} />);
5561
expect(zeroPagesPager.type()).toEqual(null);
5662

57-
const negativePagesPager = shallow(
58-
<ModuleFinderPagerComponent {...defaultProps} totalNumPages={-1} />,
59-
);
63+
const negativePagesPager = shallow(<ModuleFinderPager {...defaultProps} totalNumPages={-1} />);
6064
expect(negativePagesPager.type()).toEqual(null);
6165
});
6266

63-
test('should contain pager buttons', () => {
64-
const onDesktop = shallow(<ModuleFinderPagerComponent {...defaultProps} />);
65-
expect(onDesktop.find(ModuleFinderPagerButton)).toHaveLength(5);
66-
67-
const onMobile = shallow(<ModuleFinderPagerComponent {...defaultProps} matchBreakpoint />);
68-
expect(onMobile.find(ModuleFinderPagerButton)).toHaveLength(4);
69-
});
70-
71-
test('should respond to clicks on buttons on desktop', () => {
72-
const props = {
73-
...defaultProps,
74-
onGoToFirst: jest.fn(),
75-
onGoToPrevious: jest.fn(),
76-
onGoToPage: jest.fn(),
77-
onGoToNext: jest.fn(),
78-
onGoToLast: jest.fn(),
79-
matchBreakpoint: DESKTOP,
80-
};
81-
82-
const actual = shallow(<ModuleFinderPagerComponent {...props} />);
83-
actual.find(ModuleFinderPagerButton).forEach((n) => n.simulate('click'));
84-
expect(props.onGoToFirst).toHaveBeenCalled();
85-
expect(props.onGoToPrevious).toHaveBeenCalled();
86-
expect(props.onGoToPage).toHaveBeenCalledWith(1);
87-
expect(props.onGoToNext).toHaveBeenCalled();
88-
expect(props.onGoToLast).toHaveBeenCalled();
67+
describe('when on desktop', () => {
68+
beforeAll(() => {
69+
mockWindowMatchMedia({ matches: DESKTOP });
70+
});
71+
72+
test('should contain pager buttons', () => {
73+
const onDesktop = shallow(<ModuleFinderPager {...defaultProps} />);
74+
expect(onDesktop.find(ModuleFinderPagerButton)).toHaveLength(5);
75+
});
76+
77+
test('should respond to clicks on buttons', () => {
78+
const props = {
79+
...defaultProps,
80+
onGoToFirst: jest.fn(),
81+
onGoToPrevious: jest.fn(),
82+
onGoToPage: jest.fn(),
83+
onGoToNext: jest.fn(),
84+
onGoToLast: jest.fn(),
85+
};
86+
87+
const actual = shallow(<ModuleFinderPager {...props} />);
88+
actual.find(ModuleFinderPagerButton).forEach((n) => n.simulate('click'));
89+
expect(props.onGoToFirst).toHaveBeenCalled();
90+
expect(props.onGoToPrevious).toHaveBeenCalled();
91+
expect(props.onGoToPage).toHaveBeenCalledWith(1);
92+
expect(props.onGoToNext).toHaveBeenCalled();
93+
expect(props.onGoToLast).toHaveBeenCalled();
94+
});
8995
});
9096

91-
test('should respond to clicks on buttons on mobile', () => {
92-
const props = {
93-
...defaultProps,
94-
onGoToFirst: jest.fn(),
95-
onGoToPrevious: jest.fn(),
96-
onGoToPage: jest.fn(),
97-
onGoToNext: jest.fn(),
98-
onGoToLast: jest.fn(),
99-
matchBreakpoint: MOBILE,
100-
};
101-
102-
const actual = shallow(<ModuleFinderPagerComponent {...props} />);
103-
actual.find(ModuleFinderPagerButton).forEach((n) => n.simulate('click'));
104-
expect(props.onGoToFirst).toHaveBeenCalled();
105-
expect(props.onGoToPrevious).toHaveBeenCalled();
106-
expect(props.onGoToPage).not.toHaveBeenCalled(); // No page buttons
107-
expect(props.onGoToNext).toHaveBeenCalled();
108-
expect(props.onGoToLast).toHaveBeenCalled();
97+
describe('when on mobile', () => {
98+
beforeAll(() => {
99+
mockWindowMatchMedia({ matches: MOBILE });
100+
});
101+
102+
test('should contain pager buttons', () => {
103+
const onMobile = shallow(<ModuleFinderPager {...defaultProps} />);
104+
expect(onMobile.find(ModuleFinderPagerButton)).toHaveLength(4);
105+
});
106+
107+
test('should respond to clicks on buttons', () => {
108+
const props = {
109+
...defaultProps,
110+
onGoToFirst: jest.fn(),
111+
onGoToPrevious: jest.fn(),
112+
onGoToPage: jest.fn(),
113+
onGoToNext: jest.fn(),
114+
onGoToLast: jest.fn(),
115+
};
116+
117+
const actual = shallow(<ModuleFinderPager {...props} />);
118+
actual.find(ModuleFinderPagerButton).forEach((n) => n.simulate('click'));
119+
expect(props.onGoToFirst).toHaveBeenCalled();
120+
expect(props.onGoToPrevious).toHaveBeenCalled();
121+
expect(props.onGoToPage).not.toHaveBeenCalled(); // No page buttons
122+
expect(props.onGoToNext).toHaveBeenCalled();
123+
expect(props.onGoToLast).toHaveBeenCalled();
124+
});
109125
});
110126
});

website/src/views/modules/ModuleFinderPager.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import * as React from 'react';
1+
import { FC, memo } from 'react';
22
import classnames from 'classnames';
33
import { range } from 'lodash';
44

55
import { breakpointDown } from 'utils/css';
6-
import makeResponsive from 'views/hocs/makeResponsive';
6+
import useMediaQuery from 'views/hooks/useMediaQuery';
77
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'react-feather';
88
import { PagerProps, FIRST_PAGE_INDEX } from 'views/components/searchkit/Pagination';
99
import ModuleFinderPagerButton from 'views/modules/ModuleFinderPagerButton';
@@ -28,20 +28,17 @@ export function displayPageRange(
2828
return { firstPageNum, lastPageNum };
2929
}
3030

31-
type Props = PagerProps & {
32-
readonly matchBreakpoint: boolean;
33-
};
34-
35-
export const ModuleFinderPagerComponent: React.FC<Props> = ({
31+
const ModuleFinderPager: FC<PagerProps> = ({
3632
selectedPage,
3733
totalNumPages,
3834
onGoToFirst,
3935
onGoToPrevious,
4036
onGoToPage,
4137
onGoToNext,
4238
onGoToLast,
43-
matchBreakpoint,
4439
}) => {
40+
const matchBreakpoint = useMediaQuery(breakpointDown('md'));
41+
4542
if (totalNumPages <= 0) return null;
4643

4744
function renderDesktopPages() {
@@ -107,5 +104,4 @@ export const ModuleFinderPagerComponent: React.FC<Props> = ({
107104
);
108105
};
109106

110-
const ModuleFinderPager = makeResponsive(ModuleFinderPagerComponent, breakpointDown('md'));
111-
export default ModuleFinderPager;
107+
export default memo(ModuleFinderPager);

0 commit comments

Comments
 (0)