Skip to content
This repository was archived by the owner on Dec 30, 2022. It is now read-only.

Commit df9cbd8

Browse files
authored
feat(hooks): implement Menu component (#3405)
* feat(hooks): implement Menu component FX-1156 * address feedback * rename test * test * userEvent
1 parent b4bd933 commit df9cbd8

File tree

16 files changed

+2313
-109
lines changed

16 files changed

+2313
-109
lines changed

examples/hooks/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Hits,
1212
HitsPerPage,
1313
InfiniteHits,
14+
Menu,
1415
Pagination,
1516
RefinementList,
1617
PoweredBy,
@@ -22,7 +23,6 @@ import {
2223
import {
2324
Breadcrumb,
2425
HierarchicalMenu,
25-
Menu,
2626
NumericMenu,
2727
Panel,
2828
QueryRuleContext,

examples/hooks/components/Menu.tsx

Lines changed: 0 additions & 62 deletions
This file was deleted.

examples/hooks/components/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
export * from './Breadcrumb';
22
export * from './HierarchicalMenu';
3-
export * from './Menu';
43
export * from './NumericMenu';
54
export * from './Panel';
65
export * from './QueryRuleContext';
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import React from 'react';
2+
3+
import { cx } from './lib/cx';
4+
import { ShowMoreButton } from './ShowMoreButton';
5+
6+
import type { CreateURL } from 'instantsearch.js';
7+
import type { MenuItem } from 'instantsearch.js/es/connectors/menu/connectMenu';
8+
9+
export type MenuProps = React.HTMLAttributes<HTMLDivElement> & {
10+
items: MenuItem[];
11+
classNames?: Partial<MenuCSSClasses>;
12+
showMore?: boolean;
13+
canToggleShowMore: boolean;
14+
onToggleShowMore: () => void;
15+
isShowingMore: boolean;
16+
createURL: CreateURL<MenuItem['value']>;
17+
onRefine: (item: MenuItem) => void;
18+
};
19+
20+
export type MenuCSSClasses = {
21+
/**
22+
* Class names to apply to the root element
23+
*/
24+
root: string;
25+
/**
26+
* Class names to apply to the list element
27+
*/
28+
list: string;
29+
/**
30+
* Class names to apply to each item element
31+
*/
32+
item: string;
33+
/**
34+
* Class names to apply to each selected item element
35+
*/
36+
itemSelected: string;
37+
/**
38+
* Class names to apply to each link element
39+
*/
40+
link: string;
41+
/**
42+
* Class names to apply to each label element
43+
*/
44+
label: string;
45+
/**
46+
* Class names to apply to each facet count element
47+
*/
48+
count: string;
49+
/**
50+
* Class names to apply to the "Show more" button
51+
*/
52+
showMore: string;
53+
/**
54+
* Class names to apply to the "Show more" button if it's disabled
55+
*/
56+
showMoreDisabled: string;
57+
};
58+
59+
export function Menu({
60+
items,
61+
classNames = {},
62+
showMore,
63+
canToggleShowMore,
64+
onToggleShowMore,
65+
isShowingMore,
66+
createURL,
67+
onRefine,
68+
...props
69+
}: MenuProps) {
70+
return (
71+
<div
72+
{...props}
73+
className={cx('ais-Menu', classNames.root, props.className)}
74+
>
75+
<ul className={cx('ais-Menu-list', classNames.list)}>
76+
{items.map((item) => (
77+
<li
78+
key={item.label}
79+
className={cx(
80+
'ais-Menu-item',
81+
classNames.item,
82+
item.isRefined &&
83+
cx('ais-Menu-item--selected', classNames.itemSelected)
84+
)}
85+
>
86+
<a
87+
className={cx('ais-Menu-link', classNames.link)}
88+
href={createURL(item.value)}
89+
onClick={(event) => {
90+
event.preventDefault();
91+
onRefine(item);
92+
}}
93+
>
94+
<span className={cx('ais-Menu-label', classNames.label)}>
95+
{item.label}
96+
</span>
97+
<span className={cx('ais-Menu-count', classNames.count)}>
98+
{item.count}
99+
</span>
100+
</a>
101+
</li>
102+
))}
103+
</ul>
104+
{showMore && (
105+
<ShowMoreButton
106+
className={cx(
107+
'ais-Menu-showMore',
108+
classNames.showMore,
109+
!canToggleShowMore &&
110+
cx('ais-Menu-showMore--disabled', classNames.showMoreDisabled)
111+
)}
112+
disabled={!canToggleShowMore}
113+
onClick={onToggleShowMore}
114+
isShowingMore={isShowingMore}
115+
/>
116+
)}
117+
</div>
118+
);
119+
}

packages/react-instantsearch-hooks-dom/src/ui/RefinementList.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react';
33

44
import { Highlight } from './Highlight';
55
import { cx } from './lib/cx';
6+
import { ShowMoreButton } from './ShowMoreButton';
67

78
import type { RefinementListItem } from 'instantsearch.js/es/connectors/refinement-list/connectRefinementList';
89

@@ -12,7 +13,10 @@ export type RefinementListProps = React.HTMLAttributes<HTMLDivElement> & {
1213
query: string;
1314
searchBox?: React.ReactNode;
1415
noResults?: React.ReactNode;
15-
showMoreButton?: React.ReactNode;
16+
showMore?: boolean;
17+
canToggleShowMore: boolean;
18+
onToggleShowMore: () => void;
19+
isShowingMore: boolean;
1620
classNames?: Partial<RefinementListClassNames>;
1721
};
1822

@@ -57,6 +61,14 @@ export type RefinementListClassNames = {
5761
* Class names to apply to the facet count of each item
5862
*/
5963
count: string;
64+
/**
65+
* Class names to apply to the "Show more" button
66+
*/
67+
showMore: string;
68+
/**
69+
* Class names to apply to the "Show more" button if it's disabled
70+
*/
71+
showMoreDisabled: string;
6072
};
6173

6274
export function RefinementList({
@@ -65,7 +77,10 @@ export function RefinementList({
6577
query,
6678
searchBox,
6779
noResults,
68-
showMoreButton,
80+
showMore,
81+
canToggleShowMore,
82+
onToggleShowMore,
83+
isShowingMore,
6984
className,
7085
classNames = {},
7186
...props
@@ -144,7 +159,22 @@ export function RefinementList({
144159
))}
145160
</ul>
146161
)}
147-
{showMoreButton}
162+
{showMore && (
163+
<ShowMoreButton
164+
className={cx(
165+
'ais-RefinementList-showMore',
166+
classNames.showMore,
167+
!canToggleShowMore &&
168+
cx(
169+
'ais-RefinementList-showMore--disabled',
170+
classNames.showMoreDisabled
171+
)
172+
)}
173+
disabled={!canToggleShowMore}
174+
onClick={onToggleShowMore}
175+
isShowingMore={isShowingMore}
176+
/>
177+
)}
148178
</div>
149179
);
150180
}

packages/react-instantsearch-hooks-dom/src/ui/__tests__/InfiniteHits.test.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { act, fireEvent, render } from '@testing-library/react';
1+
import { act, render } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
23
import React from 'react';
34

45
import { InfiniteHits } from '../InfiniteHits';
@@ -240,7 +241,7 @@ describe('InfiniteHits', () => {
240241
);
241242

242243
act(() => {
243-
fireEvent.click(
244+
userEvent.click(
244245
container.querySelector('.ais-InfiniteHits-loadPrevious')!
245246
);
246247
});
@@ -257,7 +258,7 @@ describe('InfiniteHits', () => {
257258
);
258259

259260
act(() => {
260-
fireEvent.click(container.querySelector('.ais-InfiniteHits-loadMore')!);
261+
userEvent.click(container.querySelector('.ais-InfiniteHits-loadMore')!);
261262
});
262263

263264
expect(onShowMore).toHaveBeenCalledTimes(1);

0 commit comments

Comments
 (0)