Skip to content

Commit 3591fbd

Browse files
authored
Support renderItem in vanilla JS (#198)
* Support renderItem in vanilla JS * Address comments
1 parent f977505 commit 3591fbd

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { ReactNode, useContext, useMemo, useEffect, useRef, isValidElement } from 'react';
2+
import { Item } from '../../../types';
3+
import { CioAutocompleteContext } from '../CioAutocompleteProvider';
4+
5+
interface CustomItemProps {
6+
item: Item;
7+
renderItem: (props: {
8+
item: Item;
9+
query: string;
10+
getItemProps: (item: Item) => any;
11+
}) => HTMLElement | ReactNode;
12+
query: string;
13+
}
14+
15+
export default function CustomSectionItem(props: CustomItemProps) {
16+
const { renderItem, item, query } = props;
17+
const { getItemProps } = useContext(CioAutocompleteContext);
18+
const ref = useRef<HTMLDivElement>(null);
19+
20+
// eslint-disable-next-line react-hooks/exhaustive-deps
21+
const customElement = useMemo(() => renderItem({ item, query, getItemProps }), [item]);
22+
23+
const isDomElement = customElement instanceof HTMLElement;
24+
const isReactNode = isValidElement(customElement);
25+
26+
useEffect(() => {
27+
if (isDomElement && ref.current) {
28+
ref.current.innerHTML = ''; // Clear previous content
29+
ref.current.appendChild(customElement);
30+
}
31+
// eslint-disable-next-line react-hooks/exhaustive-deps
32+
}, [item]);
33+
34+
if (isReactNode) {
35+
return customElement;
36+
}
37+
38+
if (isDomElement) {
39+
return <div {...getItemProps(item)} ref={ref} />;
40+
}
41+
return null;
42+
}

src/components/Autocomplete/SectionItemsList/SectionItemsList.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { ReactElement, useContext } from 'react';
22
import { Section } from '../../../types';
33
import SectionItem from '../SectionItem/SectionItem';
4+
import CustomSectionItem from '../SectionItem/CustomSectionItem';
45
import { camelToStartCase, translate } from '../../../utils';
56
import { CioAutocompleteContext } from '../CioAutocompleteProvider';
67
import NoResults from '../AutocompleteResults/NoResults';
@@ -17,7 +18,7 @@ type SectionItemsListProps = {
1718

1819
// eslint-disable-next-line func-names
1920
const DefaultRenderSectionItemsList: RenderSectionItemsList = function ({ section }) {
20-
const { getSectionProps, query, getFormProps, advancedParameters, getItemProps } =
21+
const { getSectionProps, query, getFormProps, advancedParameters } =
2122
useContext(CioAutocompleteContext);
2223
const { displayShowAllResultsButton, translations } = advancedParameters || {};
2324
const { onSubmit } = getFormProps();
@@ -63,7 +64,14 @@ const DefaultRenderSectionItemsList: RenderSectionItemsList = function ({ sectio
6364
<ul className='cio-section-items' role='none'>
6465
{section?.data?.map((item) => {
6566
if (typeof section?.renderItem === 'function') {
66-
return section.renderItem({ item, query, getItemProps });
67+
return (
68+
<CustomSectionItem
69+
renderItem={section.renderItem}
70+
item={item}
71+
query={query}
72+
key={item.id}
73+
/>
74+
);
6775
}
6876
return (
6977
<SectionItem

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,5 +262,6 @@ export const translationsDescription = `Pass a \`translations\` object to displa
262262

263263
export const customRenderItemDescription = `Customize the rendering of individual items within a Section by providing a \`renderItem\` function. This function allows you to define how each item should be rendered.
264264
- Make sure to call \`getItemProps(item)\` and spread like this \`<div {...getItemProps(item)}/>\` on the container of each custom rendered item or else tracking will not work.
265+
- Supports both ReactNode and HTMLElement as a return type
265266
`;
266267
export const displayShowAllResultsButtonDescription = `Pass a boolean to \`displayShowAllResultsButton\` to display a button at the bottom of the Products section to show all results. This button will submit the form and trigger the \`onSubmit\` callback.`;

0 commit comments

Comments
 (0)