Skip to content

Commit a79e52b

Browse files
authored
[CSL-2060] Utilize UI autocomplete in React example (#8)
* WIP: Use OS UI Library * Use default sections * Adjust grid * Remove blank line * Update styles
1 parent 2119bcb commit a79e52b

File tree

5 files changed

+176
-150
lines changed

5 files changed

+176
-150
lines changed

package-lock.json

Lines changed: 28 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
},
1212
"dependencies": {
1313
"@constructor-io/constructorio-client-javascript": "^2.29.8",
14+
"@constructor-io/constructorio-ui-autocomplete": "^1.3.1",
1415
"@testing-library/jest-dom": "^4.2.4",
1516
"@testing-library/react": "^9.5.0",
1617
"@testing-library/user-event": "^7.2.1",
17-
"downshift": "^6.1.7",
1818
"react": "^18.2.0",
1919
"react-dom": "^18.2.0",
2020
"react-modal": "^3.16.1",

src/Layout.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function Layout() {
6161

6262
return (
6363
<div className="text-lg sm:text-base">
64-
<div className="flex flex-col sm:flex-row justify-between sm:ml-auto sm:mr-auto mb-2 md:mb-5">
64+
<div className="flex flex-col sm:flex-row justify-between sm:ml-auto sm:mr-auto mb-2 md:mb-5 relative">
6565
<ConstructorLogo />
6666
<AutocompleteSearch />
6767
</div>

src/components/AutocompleteSearch/index.jsx

Lines changed: 14 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,28 @@
11
/* eslint-disable react/jsx-props-no-spreading */
2-
import { useCombobox } from 'downshift';
3-
import React, { useEffect, useState } from 'react';
4-
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
5-
import { fetchAutoCompleteResults } from '../../utils';
6-
import { loadStatuses } from '../../utils/constants';
2+
import React from 'react';
3+
import { useNavigate } from 'react-router-dom';
4+
import { CioAutocomplete } from '@constructor-io/constructorio-ui-autocomplete';
5+
import cioClient from '../../app/cioClient';
76

87
function AutocompleteSearch() {
9-
const [params] = useSearchParams();
10-
const location = useLocation();
11-
const query = params.get('q');
12-
const [searchTerm, setSearchTerm] = useState(query || '');
138
const navigate = useNavigate();
14-
const [products, setProducts] = useState([]);
15-
const [searchSuggestions, setSearchSuggestions] = useState([]);
16-
const [, setLoadStatus] = useState(loadStatuses.SUCCESS);
17-
18-
const itemToString = (item) => (item ? item.name : '');
19-
20-
const {
21-
isOpen,
22-
getMenuProps,
23-
getItemProps,
24-
getInputProps,
25-
getComboboxProps,
26-
toggleMenu,
27-
} = useCombobox({
28-
items: [...searchSuggestions, ...products],
29-
itemToString,
30-
onInputValueChange: async ({ inputValue }) => {
31-
setSearchTerm(inputValue);
32-
},
33-
onSelectedItemChange({ selectedItem }) {
34-
// Item is product - do nothing
35-
if (selectedItem.data.id) {
36-
return;
37-
}
38-
39-
setSearchTerm(selectedItem.value);
40-
navigate(`/search?q=${selectedItem.value}`);
41-
},
42-
});
439

4410
const submitSearch = (e) => {
45-
e.preventDefault();
46-
navigate(`/search?q=${searchTerm}`);
47-
toggleMenu();
48-
};
49-
50-
let timeout;
51-
useEffect(() => {
52-
if (!searchTerm) {
53-
setProducts([]);
54-
setSearchSuggestions([]);
55-
} else {
56-
(async () => {
57-
timeout = setTimeout(async () => {
58-
try {
59-
setLoadStatus(loadStatuses.LOADING);
60-
const res = await fetchAutoCompleteResults(searchTerm);
61-
if (res.sections.Products?.length) {
62-
setProducts(res.sections.Products);
63-
}
64-
if (res.sections['Search Suggestions']?.length) {
65-
setSearchSuggestions(res.sections['Search Suggestions']);
66-
}
67-
} catch (error) {
68-
setLoadStatus(loadStatuses.FAILED);
69-
}
70-
setLoadStatus(loadStatuses.SUCCESS);
71-
}, 300);
72-
})();
73-
}
74-
75-
return () => {
76-
window.clearTimeout(timeout);
77-
};
78-
}, [searchTerm]);
11+
const { query, item } = e;
7912

80-
useEffect(() => {
81-
if (location.pathname.includes('browse')) {
82-
setSearchTerm('');
13+
if (query) {
14+
navigate(`/search?q=${query}`);
15+
} else if (item?.value) {
16+
navigate(`/search?q=${item?.value}`);
8317
}
84-
}, [location]);
18+
};
8519

8620
return (
87-
<form
88-
id="search-form"
21+
<CioAutocomplete
22+
cioJsClient={ cioClient }
8923
onSubmit={ submitSearch }
90-
className="flex place-items-center"
91-
>
92-
93-
<div className="relative w-full">
94-
<div { ...getComboboxProps() }>
95-
<input
96-
{ ...getInputProps() }
97-
value={ searchTerm }
98-
className="bg-gray-200 w-full sm:w-96 border-2 border-gray-200 rounded-3xl px-4 text-gray-700
99-
leading-tight focus:outline-none focus:bg-white focus:border-blue-800 mb-0 mt-2 md:mt-0 p-3"
100-
id="search-input"
101-
placeholder="Search..."
102-
data-cnstrc-search-input
103-
/>
104-
</div>
105-
<div data-cnstrc-autosuggest>
106-
<ul
107-
{ ...getMenuProps() }
108-
className="shadow-lg absolute right-0 py-8 px-8 list-none z-50 bg-white w-full md:w-[600px] lg:w-[1000px] flex flex-col md:flex-row gap-4 sm:gap-2 border"
109-
style={ !isOpen || !searchTerm ? { display: 'none' } : { } }
110-
>
111-
{isOpen && (
112-
<div className="basis-1/3">
113-
<div className="text-lg font-bold mb-2">Search Suggestions</div>
114-
{searchSuggestions.map((item, index) => (
115-
<li
116-
data-cnstrc-item-section="Search Suggestions"
117-
data-cnstrc-item-name={ item.value }
118-
data-cnstrc-item-id={ item.data?.id }
119-
className="mb-1 cursor-pointer hover:underline"
120-
key={ `${item.value} ${item.data?.id} ` }
121-
{ ...getItemProps({ item, index }) }
122-
>
123-
{item.value}
124-
</li>
125-
))}
126-
</div>
127-
)}
128-
129-
{isOpen && (
130-
<div>
131-
<div className="text-lg font-bold mb-2">Products</div>
132-
<div className="basis-2/3 grid gap-4 grid-cols-3">
133-
{products.map((item, index) => (
134-
<li
135-
data-cnstrc-item-section="Products"
136-
data-cnstrc-item-name={ item.value }
137-
data-cnstrc-item-id={ item.data?.id }
138-
className="basis-1/3 flex flex-col content-center space-x-2"
139-
key={ `${item.value} ${item.data?.id} ` }
140-
{ ...getItemProps({ item, index: (index + searchSuggestions.length) }) }
141-
>
142-
<div className="hover:underline flex flex-col items-center sm:items-start">
143-
<img width="200" src={ item.data?.image_url } alt="" />
144-
<div className="text-sm">
145-
{item.value}
146-
</div>
147-
</div>
148-
</li>
149-
))}
150-
</div>
151-
</div>
152-
)}
153-
</ul>
154-
</div>
155-
</div>
156-
</form>
24+
placeholder="Search..."
25+
/>
15726
);
15827
}
15928

0 commit comments

Comments
 (0)