Skip to content

Commit bcf3e38

Browse files
klesaulnierLE SAULNIER Kevinjamal-khey
authored
Handle Paginated Endpoint for Search Functionality (#503)
* Add PaperComponent prop to ElementSearchInput --------- Signed-off-by: LE SAULNIER Kevin <[email protected]> Co-authored-by: LE SAULNIER Kevin <[email protected]> Co-authored-by: jamal-khey <[email protected]>
1 parent 8383802 commit bcf3e38

File tree

4 files changed

+61
-8
lines changed

4 files changed

+61
-8
lines changed

src/components/ElementSearchDialog/element-search-input.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ export type RenderElementProps<T> = HTMLAttributes<HTMLLIElement> & {
2020
export interface ElementSearchInputProps<T>
2121
extends Pick<
2222
AutocompleteProps<T, false, boolean, true>,
23-
'sx' | 'size' | 'loadingText' | 'loading' | 'disableClearable'
23+
| 'sx'
24+
| 'size'
25+
| 'loadingText'
26+
| 'loading'
27+
| 'disableClearable'
28+
| 'getOptionDisabled'
29+
| 'PaperComponent'
2430
> {
2531
searchTerm: string;
2632
onSearchTermChange: (searchTerm: string) => void;
@@ -38,7 +44,9 @@ export interface ElementSearchInputProps<T>
3844
showResults?: boolean;
3945
}
4046

41-
export function ElementSearchInput<T>(props: ElementSearchInputProps<T>) {
47+
export function ElementSearchInput<T>(
48+
props: Readonly<ElementSearchInputProps<T>>
49+
) {
4250
const {
4351
elementsFound,
4452
loading,
@@ -56,6 +64,7 @@ export function ElementSearchInput<T>(props: ElementSearchInputProps<T>) {
5664
size,
5765
sx,
5866
disableClearable,
67+
PaperComponent,
5968
} = props;
6069

6170
const intl = useIntl();
@@ -131,6 +140,7 @@ export function ElementSearchInput<T>(props: ElementSearchInputProps<T>) {
131140
return option === value;
132141
}}
133142
disabled={searchTermDisabled}
143+
PaperComponent={PaperComponent}
134144
/>
135145
);
136146
}

src/components/ElementSearchDialog/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export { default as TagRenderer } from './tag-renderer';
99
export type { TagRendererProps } from './tag-renderer';
1010
export { ElementSearchInput } from './element-search-input';
1111
export { default as useElementSearch } from './use-element-search';
12+
export type { Paginated } from './use-element-search';

src/components/ElementSearchDialog/use-element-search.tsx

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,43 @@ import { useSnackMessage } from '../../hooks/useSnackMessage';
1111

1212
const SEARCH_FETCH_TIMEOUT_MILLIS = 1000;
1313

14+
export type Paginated<T> = {
15+
content: T[];
16+
totalPages: number;
17+
totalElements: number;
18+
last: boolean;
19+
size: number;
20+
number: number;
21+
sort: {
22+
sorted: boolean;
23+
unsorted: boolean;
24+
empty: boolean;
25+
};
26+
first: boolean;
27+
numberOfElements: number;
28+
empty: boolean;
29+
};
30+
1431
interface UseElementSearch<T> {
15-
fetchElements: (newSearchTerm: string) => Promise<T[]>;
32+
fetchElements: (newSearchTerm: string) => Promise<Paginated<T> | T[]>;
1633
}
1734

18-
const useElementSearch = <T,>(props: UseElementSearch<T>) => {
35+
const useElementSearch = <T,>(
36+
props: UseElementSearch<T>
37+
): {
38+
searchTerm: string;
39+
updateSearchTerm: (newSearchTerm: string) => void;
40+
elementsFound: T[];
41+
isLoading: boolean;
42+
totalElements: number;
43+
} => {
1944
const { fetchElements } = props;
2045

2146
const { snackError } = useSnackMessage();
2247
const [isLoading, setIsLoading] = useState(false);
2348
const [searchTerm, setSearchTerm] = useState('');
24-
const [elementsFound, setElementsFound] = useState<T[]>([]);
49+
const [elementsFound, setElementsFound] = useState<T[]>([] as T[]);
50+
const [totalElements, setTotalElements] = useState(0);
2551
const lastSearchTermRef = useRef('');
2652

2753
const searchMatchingElements = useCallback(
@@ -31,17 +57,25 @@ const useElementSearch = <T,>(props: UseElementSearch<T>) => {
3157
}
3258

3359
lastSearchTermRef.current = newSearchTerm;
60+
3461
fetchElements(newSearchTerm)
3562
.then((infos) => {
3663
if (newSearchTerm === lastSearchTermRef.current) {
37-
setElementsFound(infos);
64+
if (Array.isArray(infos)) {
65+
setElementsFound(infos);
66+
setTotalElements(infos.length);
67+
} else {
68+
setElementsFound(infos.content);
69+
setTotalElements(infos.totalElements);
70+
}
3871
setIsLoading(false);
3972
} // else ignore results of outdated fetch
4073
})
4174
.catch((error) => {
4275
// else ignore errors of outdated fetch if changing "isLoading state"
4376
if (newSearchTerm === lastSearchTermRef.current) {
4477
setElementsFound([]);
78+
setTotalElements(0);
4579
setIsLoading(false);
4680
snackError({
4781
messageTxt: error.message,
@@ -50,7 +84,7 @@ const useElementSearch = <T,>(props: UseElementSearch<T>) => {
5084
}
5185
});
5286
},
53-
[snackError, fetchElements]
87+
[fetchElements, snackError]
5488
);
5589

5690
const debouncedSearchMatchingElements = useDebounce(
@@ -66,6 +100,7 @@ const useElementSearch = <T,>(props: UseElementSearch<T>) => {
66100
// still debouncing to cancel previous call, otherwise last call will still trigger
67101
if (newSearchTerm.length === 0) {
68102
setElementsFound([]);
103+
setTotalElements(0);
69104
setIsLoading(false);
70105
} else {
71106
setIsLoading(true);
@@ -76,7 +111,13 @@ const useElementSearch = <T,>(props: UseElementSearch<T>) => {
76111
[debouncedSearchMatchingElements]
77112
);
78113

79-
return { searchTerm, updateSearchTerm, elementsFound, isLoading };
114+
return {
115+
searchTerm,
116+
updateSearchTerm,
117+
elementsFound,
118+
isLoading,
119+
totalElements,
120+
};
80121
};
81122

82123
export default useElementSearch;

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ export {
179179
ElementSearchInput,
180180
useElementSearch,
181181
} from './components/ElementSearchDialog';
182+
export type { Paginated } from './components/ElementSearchDialog';
182183
export type { TagRendererProps } from './components/ElementSearchDialog';
183184
export { EquipmentItem } from './components/ElementSearchDialog/equipment-item';
184185
export { default as CardErrorBoundary } from './components/CardErrorBoundary';

0 commit comments

Comments
 (0)