@constructor-io/constructorio-ui-autocomplete 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/lib/cjs/components/Autocomplete/SectionItem/SectionItem.js +6 -1
- package/lib/cjs/components/Autocomplete/SectionItemsList/SectionItemsList.js +1 -4
- package/lib/cjs/constants.js +8 -3
- package/lib/cjs/hooks/useCioAutocomplete.js +13 -6
- package/lib/cjs/hooks/useDebouncedFetchSections.js +33 -5
- package/lib/cjs/hooks/useFetchRecommendationPod.js +4 -1
- package/lib/cjs/typeGuards.js +11 -4
- package/lib/cjs/utils.js +5 -17
- package/lib/mjs/components/Autocomplete/SectionItem/SectionItem.js +7 -2
- package/lib/mjs/components/Autocomplete/SectionItemsList/SectionItemsList.js +1 -1
- package/lib/mjs/constants.js +7 -2
- package/lib/mjs/hooks/useCioAutocomplete.js +13 -6
- package/lib/mjs/hooks/useDebouncedFetchSections.js +37 -8
- package/lib/mjs/hooks/useFetchRecommendationPod.js +1 -0
- package/lib/mjs/typeGuards.js +7 -3
- package/lib/mjs/utils.js +4 -15
- package/lib/styles.css +4 -0
- package/lib/types/constants.d.ts +4 -1
- package/lib/types/hooks/useCioAutocomplete.d.ts +2 -2
- package/lib/types/hooks/useDebouncedFetchSections.d.ts +2 -2
- package/lib/types/typeGuards.d.ts +3 -1
- package/lib/types/types.d.ts +22 -15
- package/lib/types/utils.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,9 +82,9 @@ function YourComponent() {
|
|
|
82
82
|
</div>
|
|
83
83
|
<div className='cio-items'>
|
|
84
84
|
{section?.data?.map((item) => (
|
|
85
|
-
<div {...getItemProps(item)} key={item?.
|
|
85
|
+
<div {...getItemProps(item)} key={item?.id}>
|
|
86
86
|
<div>
|
|
87
|
-
{
|
|
87
|
+
{item.data?.image_url && (
|
|
88
88
|
<img
|
|
89
89
|
width='100%'
|
|
90
90
|
src={item.data?.image_url}
|
|
@@ -92,7 +92,11 @@ function YourComponent() {
|
|
|
92
92
|
data-testid='cio-img'
|
|
93
93
|
/>
|
|
94
94
|
)}
|
|
95
|
-
|
|
95
|
+
{item.groupName ? (
|
|
96
|
+
<p className='cio-term-in-group'>in {item.groupName}</p>
|
|
97
|
+
) : (
|
|
98
|
+
<p>{item.value}</p>
|
|
99
|
+
)}
|
|
96
100
|
</div>
|
|
97
101
|
</div>
|
|
98
102
|
))}
|
|
@@ -14,8 +14,13 @@ function SectionItem(props) {
|
|
|
14
14
|
react_1.default.createElement("img", { "data-testid": 'cio-img', src: (_a = item.data) === null || _a === void 0 ? void 0 : _a.image_url, alt: item.value }),
|
|
15
15
|
react_1.default.createElement("p", { "data-testid": 'cio-text' }, item.value)));
|
|
16
16
|
}
|
|
17
|
+
else if ((0, typeGuards_1.isInGroupSuggestion)(item)) {
|
|
18
|
+
defaultChildren = react_1.default.createElement("p", { className: 'cio-term-in-group' },
|
|
19
|
+
"in ",
|
|
20
|
+
item.groupName);
|
|
21
|
+
}
|
|
17
22
|
else {
|
|
18
|
-
defaultChildren = item.value;
|
|
23
|
+
defaultChildren = react_1.default.createElement("p", null, item.value);
|
|
19
24
|
}
|
|
20
25
|
return react_1.default.createElement("li", Object.assign({}, getItemProps(item)), children || defaultChildren);
|
|
21
26
|
}
|
|
@@ -12,10 +12,7 @@ const DefaultRenderSectionItemsList = function ({ section }) {
|
|
|
12
12
|
return null;
|
|
13
13
|
return (react_1.default.createElement("li", { className: `${sectionName} cio-section`, role: 'none' },
|
|
14
14
|
react_1.default.createElement("h5", { className: 'cio-sectionName', "aria-hidden": true }, (0, utils_1.camelToStartCase)(sectionName)),
|
|
15
|
-
react_1.default.createElement("ul", { className: 'cio-section-items', role: 'none' }, (_b = section === null || section === void 0 ? void 0 : section.data) === null || _b === void 0 ? void 0 : _b.map((item) => {
|
|
16
|
-
var _a;
|
|
17
|
-
return (react_1.default.createElement(SectionItem_1.default, { item: item, key: `${section === null || section === void 0 ? void 0 : section.identifier}_${(_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.id}` }));
|
|
18
|
-
}))));
|
|
15
|
+
react_1.default.createElement("ul", { className: 'cio-section-items', role: 'none' }, (_b = section === null || section === void 0 ? void 0 : section.data) === null || _b === void 0 ? void 0 : _b.map((item) => (react_1.default.createElement(SectionItem_1.default, { item: item, key: item === null || item === void 0 ? void 0 : item.id }))))));
|
|
19
16
|
};
|
|
20
17
|
function SectionItemsList(props) {
|
|
21
18
|
const { section, children = DefaultRenderSectionItemsList } = props;
|
package/lib/cjs/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.customStylesDescription = exports.multipleSectionsDescription = exports.openOnFocusDescription = exports.zeroStateSectionsDescription = exports.onSubmitDefault = exports.onSubmitDescription = exports.onChangeDescription = exports.onFocusDescription = exports.customSectionDescription = exports.recommendationsDescription = exports.sectionOrderDescription = exports.numResultsDescription = exports.contentDescription = exports.productsDescription = exports.searchSuggestionsDescription = exports.placeholderDescription = exports.cioJsClientDescription = exports.apiKeyDescription = exports.zeroStateDescription = exports.userEventsDescription = exports.sectionsDescription = exports.hookDescription = exports.componentDescription = exports.apiKey = void 0;
|
|
3
|
+
exports.termsWithGroupSuggestionsDescription = exports.advancedParametersDefaultDescription = exports.advancedParametersDescription = exports.customStylesDescription = exports.multipleSectionsDescription = exports.openOnFocusDescription = exports.zeroStateSectionsDescription = exports.onSubmitDefault = exports.onSubmitDescription = exports.onChangeDescription = exports.onFocusDescription = exports.customSectionDescription = exports.recommendationsDescription = exports.sectionOrderDescription = exports.numResultsDescription = exports.contentDescription = exports.productsDescription = exports.searchSuggestionsDescription = exports.placeholderDescription = exports.cioJsClientDescription = exports.apiKeyDescription = exports.zeroStateDescription = exports.userEventsDescription = exports.sectionsDescription = exports.hookDescription = exports.componentDescription = exports.apiKey = void 0;
|
|
4
4
|
// Autocomplete key index
|
|
5
5
|
exports.apiKey = 'key_Gep3oQOu5IMcNh9A';
|
|
6
6
|
/// //////////////////////////////
|
|
@@ -33,8 +33,8 @@ const {
|
|
|
33
33
|
sections: [{...}], // array of sections data to render in menu list
|
|
34
34
|
getFormProps: () => ({...})), // prop getter for jsx form element
|
|
35
35
|
getInputProps: () => ({...})), // prop getter for jsx input element
|
|
36
|
-
getMenuProps: () => ({...})), // prop getter for jsx element
|
|
37
|
-
getItemProps: (item) => ({...})), // prop getter for jsx element
|
|
36
|
+
getMenuProps: () => ({...})), // prop getter for jsx element rendering the results container
|
|
37
|
+
getItemProps: (item) => ({...})), // prop getter for jsx element rendering each result
|
|
38
38
|
|
|
39
39
|
// available for use, but not required for all use cases
|
|
40
40
|
selectedItem: item, // undefined or current selected item (via hover or arrow keys)
|
|
@@ -186,3 +186,8 @@ import '@constructor-io/constructorio-ui-autocomplete/styles.css';
|
|
|
186
186
|
}
|
|
187
187
|
\`\`\`
|
|
188
188
|
`;
|
|
189
|
+
exports.advancedParametersDescription = `Pass an \`advancedParameters\` object to configure more options for the data included in the results requested from constructor's servers. The stories below show examples of the optional fields within this \`advancedParameters\` object that can be used to fine-tune the autosuggest data returned from constructor's servers.`;
|
|
190
|
+
exports.advancedParametersDefaultDescription = `Passing an \`advancedParameters\` object is optional. Passing an empty object for the \`advancedParameters\` field behaves the same as not passing an \`advancedParameters\` object at all. This has no impact on the results returned by default.`;
|
|
191
|
+
exports.termsWithGroupSuggestionsDescription = `Pass integers for the \`numTermsWithGroupSuggestions\` and \`numGroupsSuggestedPerTerm\` fields to add suggested group filters to search term suggestions. Not all suggested search terms will have group filters, so these integers are upper limits, used to specify the maximum number of terms with group filters and the maximum number of suggested group filters per term. To see this in action:
|
|
192
|
+
- type "bab" in the example below. Notice how the user is presented with a search term of "baby" as well as "baby in Bodysuits" and "baby in Baby Organic Cotton"
|
|
193
|
+
- navigate to the "Terms With Group Suggestions" story (using the navigation menu to the left), then use the Controls to adjust the values of \`numTermsWithGroupSuggestions\` to \`3\` and \`numGroupsSuggestedPerTerm\` to \`1\`. Next, type "dan" in the example autocomplete input field. Notice how the user is presented with three different search terms that have a maximum of one "in {group}" suggestion each`;
|
|
@@ -9,6 +9,7 @@ const useDebouncedFetchSections_1 = tslib_1.__importDefault(require("./useDeboun
|
|
|
9
9
|
const useFetchRecommendationPod_1 = tslib_1.__importDefault(require("./useFetchRecommendationPod"));
|
|
10
10
|
const usePrevious_1 = tslib_1.__importDefault(require("./usePrevious"));
|
|
11
11
|
const utils_1 = require("../utils");
|
|
12
|
+
const typeGuards_1 = require("../typeGuards");
|
|
12
13
|
exports.defaultSections = [
|
|
13
14
|
{
|
|
14
15
|
identifier: 'Search Suggestions',
|
|
@@ -21,7 +22,7 @@ exports.defaultSections = [
|
|
|
21
22
|
];
|
|
22
23
|
const useCioAutocomplete = (options) => {
|
|
23
24
|
const defaultPlaceholder = 'What can we help you find today?';
|
|
24
|
-
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = defaultPlaceholder, sections = exports.defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', } = options;
|
|
25
|
+
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = defaultPlaceholder, sections = exports.defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', advancedParameters = {}, } = options;
|
|
25
26
|
const [query, setQuery] = (0, react_1.useState)('');
|
|
26
27
|
const previousQuery = (0, usePrevious_1.default)(query);
|
|
27
28
|
const cioClient = (0, useCioClient_1.default)({ apiKey, cioJsClient });
|
|
@@ -39,14 +40,20 @@ const useCioAutocomplete = (options) => {
|
|
|
39
40
|
}
|
|
40
41
|
const autocompleteSections = activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => config.type === 'autocomplete' || !config.type);
|
|
41
42
|
const recommendationsSections = activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => config.type === 'recommendations');
|
|
42
|
-
const autocompleteResults = (0, useDebouncedFetchSections_1.default)(query, cioClient, autocompleteSections);
|
|
43
|
+
const autocompleteResults = (0, useDebouncedFetchSections_1.default)(query, cioClient, autocompleteSections, advancedParameters);
|
|
43
44
|
const recommendationsResults = (0, useFetchRecommendationPod_1.default)(cioClient, recommendationsSections);
|
|
44
45
|
const sectionResults = Object.assign(Object.assign({}, autocompleteResults), recommendationsResults);
|
|
45
46
|
const activeSectionsWithData = [];
|
|
46
47
|
activeSections === null || activeSections === void 0 ? void 0 : activeSections.forEach((config) => {
|
|
47
|
-
const { identifier
|
|
48
|
-
|
|
49
|
-
if (
|
|
48
|
+
const { identifier } = config;
|
|
49
|
+
let data;
|
|
50
|
+
if ((0, typeGuards_1.isCustomSection)(config)) {
|
|
51
|
+
data = config.data;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
data = sectionResults[identifier];
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(data)) {
|
|
50
57
|
activeSectionsWithData.push(Object.assign(Object.assign({}, config), { data }));
|
|
51
58
|
}
|
|
52
59
|
});
|
|
@@ -67,7 +74,7 @@ const useCioAutocomplete = (options) => {
|
|
|
67
74
|
openMenu,
|
|
68
75
|
closeMenu,
|
|
69
76
|
getItemProps: (item) => {
|
|
70
|
-
const { index, sectionId } = (0, utils_1.getItemPosition)({ item,
|
|
77
|
+
const { index, sectionId } = (0, utils_1.getItemPosition)({ item, items });
|
|
71
78
|
const sectionItemTestId = `cio-item-${sectionId === null || sectionId === void 0 ? void 0 : sectionId.replace(' ', '')}`;
|
|
72
79
|
return Object.assign(Object.assign({}, downshift.getItemProps({ item, index })), { className: `cio-item ${sectionItemTestId}`, 'data-testid': sectionItemTestId });
|
|
73
80
|
},
|
|
@@ -3,6 +3,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const useDebounce_1 = tslib_1.__importDefault(require("./useDebounce"));
|
|
6
|
+
const transformResponse = (response, options) => {
|
|
7
|
+
const { numTermsWithGroupSuggestions, numGroupsSuggestedPerTerm } = options;
|
|
8
|
+
const newSectionsData = {};
|
|
9
|
+
Object.keys(response.sections).forEach((section) => {
|
|
10
|
+
newSectionsData[section] = [];
|
|
11
|
+
const sectionItems = response.sections[section].map((item) => {
|
|
12
|
+
var _a;
|
|
13
|
+
return (Object.assign(Object.assign({}, item), { id: (_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.id, section }));
|
|
14
|
+
});
|
|
15
|
+
sectionItems.forEach((item, itemIndex) => {
|
|
16
|
+
var _a, _b, _c;
|
|
17
|
+
(_a = newSectionsData[section]) === null || _a === void 0 ? void 0 : _a.push(item);
|
|
18
|
+
if (section === 'Search Suggestions' &&
|
|
19
|
+
((_c = (_b = item === null || item === void 0 ? void 0 : item.data) === null || _b === void 0 ? void 0 : _b.groups) === null || _c === void 0 ? void 0 : _c.length) &&
|
|
20
|
+
itemIndex < numTermsWithGroupSuggestions) {
|
|
21
|
+
item.data.groups.forEach((group, groupIndex) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
if (groupIndex < numGroupsSuggestedPerTerm) {
|
|
24
|
+
const inGroupSearchSuggestion = Object.assign(Object.assign({}, item), { id: `${(_a = item.data) === null || _a === void 0 ? void 0 : _a.id}_${group.group_id}`, groupName: group.display_name, groupId: group.group_id });
|
|
25
|
+
(_b = newSectionsData[section]) === null || _b === void 0 ? void 0 : _b.push(inGroupSearchSuggestion);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
return newSectionsData;
|
|
32
|
+
};
|
|
6
33
|
const autocompleteParameters = {
|
|
7
34
|
resultsPerSection: {},
|
|
8
35
|
// numResults: 8,
|
|
@@ -10,18 +37,19 @@ const autocompleteParameters = {
|
|
|
10
37
|
// filters: {},
|
|
11
38
|
// variationsMap: {}
|
|
12
39
|
};
|
|
13
|
-
const useDebouncedFetchSection = (query, cioClient, autocompleteSections) => {
|
|
40
|
+
const useDebouncedFetchSection = (query, cioClient, autocompleteSections, advancedParameters) => {
|
|
14
41
|
const [sectionsData, setSectionsData] = (0, react_1.useState)({});
|
|
15
42
|
const debouncedSearchTerm = (0, useDebounce_1.default)(query);
|
|
43
|
+
const { numTermsWithGroupSuggestions = 0, numGroupsSuggestedPerTerm = 0 } = advancedParameters;
|
|
16
44
|
if (autocompleteSections) {
|
|
17
45
|
autocompleteParameters.resultsPerSection = autocompleteSections.reduce((acc, sectionConfig) => (Object.assign(Object.assign({}, acc), { [sectionConfig.identifier]: (sectionConfig === null || sectionConfig === void 0 ? void 0 : sectionConfig.numResults) || 8 })), {});
|
|
18
46
|
}
|
|
19
47
|
(0, react_1.useEffect)(() => {
|
|
20
48
|
if (debouncedSearchTerm) {
|
|
21
49
|
cioClient === null || cioClient === void 0 ? void 0 : cioClient.autocomplete.getAutocompleteResults(debouncedSearchTerm, autocompleteParameters).then((response) => {
|
|
22
|
-
const newSectionsData = {
|
|
23
|
-
|
|
24
|
-
|
|
50
|
+
const newSectionsData = transformResponse(response, {
|
|
51
|
+
numTermsWithGroupSuggestions,
|
|
52
|
+
numGroupsSuggestedPerTerm,
|
|
25
53
|
});
|
|
26
54
|
setSectionsData(newSectionsData);
|
|
27
55
|
});
|
|
@@ -29,7 +57,7 @@ const useDebouncedFetchSection = (query, cioClient, autocompleteSections) => {
|
|
|
29
57
|
else if (!debouncedSearchTerm) {
|
|
30
58
|
setSectionsData({});
|
|
31
59
|
}
|
|
32
|
-
}, [debouncedSearchTerm, cioClient]);
|
|
60
|
+
}, [debouncedSearchTerm, cioClient, numTermsWithGroupSuggestions, numGroupsSuggestedPerTerm]);
|
|
33
61
|
return sectionsData;
|
|
34
62
|
};
|
|
35
63
|
exports.default = useDebouncedFetchSection;
|
|
@@ -16,7 +16,10 @@ const useFetchRecommendationPod = (cioClient, recommendationPods) => {
|
|
|
16
16
|
responses.forEach(({ response }) => {
|
|
17
17
|
const { pod, results } = response;
|
|
18
18
|
if (pod === null || pod === void 0 ? void 0 : pod.id) {
|
|
19
|
-
recommendationPodResults[pod.id] = results === null || results === void 0 ? void 0 : results.map((item) =>
|
|
19
|
+
recommendationPodResults[pod.id] = results === null || results === void 0 ? void 0 : results.map((item) => {
|
|
20
|
+
var _a;
|
|
21
|
+
return (Object.assign(Object.assign({}, item), { id: (_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.id, section: pod.id }));
|
|
22
|
+
});
|
|
20
23
|
}
|
|
21
24
|
});
|
|
22
25
|
setRecommendationResults(recommendationPodResults);
|
package/lib/cjs/typeGuards.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isProduct = void 0;
|
|
4
|
-
// Type Guard
|
|
5
|
-
// eslint-disable-next-line
|
|
3
|
+
exports.isCustomSection = exports.isInGroupSuggestion = exports.isProduct = void 0;
|
|
6
4
|
function isProduct(item) {
|
|
7
|
-
|
|
5
|
+
var _a;
|
|
6
|
+
return ((_a = item.data) === null || _a === void 0 ? void 0 : _a.image_url) !== undefined;
|
|
8
7
|
}
|
|
9
8
|
exports.isProduct = isProduct;
|
|
9
|
+
function isInGroupSuggestion(item) {
|
|
10
|
+
return item.groupName !== undefined;
|
|
11
|
+
}
|
|
12
|
+
exports.isInGroupSuggestion = isInGroupSuggestion;
|
|
13
|
+
function isCustomSection(config) {
|
|
14
|
+
return config.data !== undefined;
|
|
15
|
+
}
|
|
16
|
+
exports.isCustomSection = isCustomSection;
|
package/lib/cjs/utils.js
CHANGED
|
@@ -3,23 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getCioClient = exports.disableStoryActions = exports.stringifyWithDefaults = exports.defaultArgumentsCode = exports.defaultOnSubmitCode = exports.getStoryParams = exports.sleep = exports.clearConstructorRequests = exports.isTrackingRequestSent = exports.camelToStartCase = exports.getItemPosition = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const constructorio_client_javascript_1 = tslib_1.__importDefault(require("@constructor-io/constructorio-client-javascript"));
|
|
6
|
-
const getItemPosition = ({ item,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const indexInSection = (_a = section.data) === null || _a === void 0 ? void 0 : _a.findIndex(({ data: result }) => {
|
|
12
|
-
var _a;
|
|
13
|
-
indexAcrossSections += 1;
|
|
14
|
-
return (result === null || result === void 0 ? void 0 : result.id) === ((_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.id);
|
|
15
|
-
});
|
|
16
|
-
if (indexInSection === -1) {
|
|
17
|
-
return false; // continue searching through sections
|
|
18
|
-
}
|
|
19
|
-
sectionId = section.identifier; // item found in current section
|
|
20
|
-
return true; // stop looping through sections
|
|
21
|
-
});
|
|
22
|
-
return { sectionId, index: indexAcrossSections };
|
|
6
|
+
const getItemPosition = ({ item, items }) => {
|
|
7
|
+
var _a;
|
|
8
|
+
const index = items.findIndex((itemInFlatList) => (itemInFlatList === null || itemInFlatList === void 0 ? void 0 : itemInFlatList.id) === (item === null || item === void 0 ? void 0 : item.id));
|
|
9
|
+
const sectionId = (_a = items[index]) === null || _a === void 0 ? void 0 : _a.section;
|
|
10
|
+
return { sectionId, index };
|
|
23
11
|
};
|
|
24
12
|
exports.getItemPosition = getItemPosition;
|
|
25
13
|
const camelToStartCase = (camelCaseString) => camelCaseString
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
2
|
import { CioAutocompleteContext } from '../CioAutocompleteProvider';
|
|
3
|
-
import { isProduct } from '../../../typeGuards';
|
|
3
|
+
import { isProduct, isInGroupSuggestion } from '../../../typeGuards';
|
|
4
4
|
export default function SectionItem(props) {
|
|
5
5
|
const { item, children } = props;
|
|
6
6
|
const { getItemProps } = useContext(CioAutocompleteContext);
|
|
@@ -10,8 +10,13 @@ export default function SectionItem(props) {
|
|
|
10
10
|
React.createElement("img", { "data-testid": 'cio-img', src: item.data?.image_url, alt: item.value }),
|
|
11
11
|
React.createElement("p", { "data-testid": 'cio-text' }, item.value)));
|
|
12
12
|
}
|
|
13
|
+
else if (isInGroupSuggestion(item)) {
|
|
14
|
+
defaultChildren = React.createElement("p", { className: 'cio-term-in-group' },
|
|
15
|
+
"in ",
|
|
16
|
+
item.groupName);
|
|
17
|
+
}
|
|
13
18
|
else {
|
|
14
|
-
defaultChildren = item.value;
|
|
19
|
+
defaultChildren = React.createElement("p", null, item.value);
|
|
15
20
|
}
|
|
16
21
|
return React.createElement("li", { ...getItemProps(item) }, children || defaultChildren);
|
|
17
22
|
}
|
|
@@ -8,7 +8,7 @@ const DefaultRenderSectionItemsList = function ({ section }) {
|
|
|
8
8
|
return null;
|
|
9
9
|
return (React.createElement("li", { className: `${sectionName} cio-section`, role: 'none' },
|
|
10
10
|
React.createElement("h5", { className: 'cio-sectionName', "aria-hidden": true }, camelToStartCase(sectionName)),
|
|
11
|
-
React.createElement("ul", { className: 'cio-section-items', role: 'none' }, section?.data?.map((item) => (React.createElement(SectionItem, { item: item, key:
|
|
11
|
+
React.createElement("ul", { className: 'cio-section-items', role: 'none' }, section?.data?.map((item) => (React.createElement(SectionItem, { item: item, key: item?.id }))))));
|
|
12
12
|
};
|
|
13
13
|
export default function SectionItemsList(props) {
|
|
14
14
|
const { section, children = DefaultRenderSectionItemsList } = props;
|
package/lib/mjs/constants.js
CHANGED
|
@@ -30,8 +30,8 @@ const {
|
|
|
30
30
|
sections: [{...}], // array of sections data to render in menu list
|
|
31
31
|
getFormProps: () => ({...})), // prop getter for jsx form element
|
|
32
32
|
getInputProps: () => ({...})), // prop getter for jsx input element
|
|
33
|
-
getMenuProps: () => ({...})), // prop getter for jsx element
|
|
34
|
-
getItemProps: (item) => ({...})), // prop getter for jsx element
|
|
33
|
+
getMenuProps: () => ({...})), // prop getter for jsx element rendering the results container
|
|
34
|
+
getItemProps: (item) => ({...})), // prop getter for jsx element rendering each result
|
|
35
35
|
|
|
36
36
|
// available for use, but not required for all use cases
|
|
37
37
|
selectedItem: item, // undefined or current selected item (via hover or arrow keys)
|
|
@@ -182,3 +182,8 @@ import '@constructor-io/constructorio-ui-autocomplete/styles.css';
|
|
|
182
182
|
}
|
|
183
183
|
\`\`\`
|
|
184
184
|
`;
|
|
185
|
+
export const advancedParametersDescription = `Pass an \`advancedParameters\` object to configure more options for the data included in the results requested from constructor's servers. The stories below show examples of the optional fields within this \`advancedParameters\` object that can be used to fine-tune the autosuggest data returned from constructor's servers.`;
|
|
186
|
+
export const advancedParametersDefaultDescription = `Passing an \`advancedParameters\` object is optional. Passing an empty object for the \`advancedParameters\` field behaves the same as not passing an \`advancedParameters\` object at all. This has no impact on the results returned by default.`;
|
|
187
|
+
export const termsWithGroupSuggestionsDescription = `Pass integers for the \`numTermsWithGroupSuggestions\` and \`numGroupsSuggestedPerTerm\` fields to add suggested group filters to search term suggestions. Not all suggested search terms will have group filters, so these integers are upper limits, used to specify the maximum number of terms with group filters and the maximum number of suggested group filters per term. To see this in action:
|
|
188
|
+
- type "bab" in the example below. Notice how the user is presented with a search term of "baby" as well as "baby in Bodysuits" and "baby in Baby Organic Cotton"
|
|
189
|
+
- navigate to the "Terms With Group Suggestions" story (using the navigation menu to the left), then use the Controls to adjust the values of \`numTermsWithGroupSuggestions\` to \`3\` and \`numGroupsSuggestedPerTerm\` to \`1\`. Next, type "dan" in the example autocomplete input field. Notice how the user is presented with three different search terms that have a maximum of one "in {group}" suggestion each`;
|
|
@@ -5,6 +5,7 @@ import useDebouncedFetchSection from './useDebouncedFetchSections';
|
|
|
5
5
|
import useFetchRecommendationPod from './useFetchRecommendationPod';
|
|
6
6
|
import usePrevious from './usePrevious';
|
|
7
7
|
import { getItemPosition } from '../utils';
|
|
8
|
+
import { isCustomSection } from '../typeGuards';
|
|
8
9
|
export const defaultSections = [
|
|
9
10
|
{
|
|
10
11
|
identifier: 'Search Suggestions',
|
|
@@ -17,7 +18,7 @@ export const defaultSections = [
|
|
|
17
18
|
];
|
|
18
19
|
const useCioAutocomplete = (options) => {
|
|
19
20
|
const defaultPlaceholder = 'What can we help you find today?';
|
|
20
|
-
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = defaultPlaceholder, sections = defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', } = options;
|
|
21
|
+
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = defaultPlaceholder, sections = defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', advancedParameters = {}, } = options;
|
|
21
22
|
const [query, setQuery] = useState('');
|
|
22
23
|
const previousQuery = usePrevious(query);
|
|
23
24
|
const cioClient = useCioClient({ apiKey, cioJsClient });
|
|
@@ -35,14 +36,20 @@ const useCioAutocomplete = (options) => {
|
|
|
35
36
|
}
|
|
36
37
|
const autocompleteSections = activeSections?.filter((config) => config.type === 'autocomplete' || !config.type);
|
|
37
38
|
const recommendationsSections = activeSections?.filter((config) => config.type === 'recommendations');
|
|
38
|
-
const autocompleteResults = useDebouncedFetchSection(query, cioClient, autocompleteSections);
|
|
39
|
+
const autocompleteResults = useDebouncedFetchSection(query, cioClient, autocompleteSections, advancedParameters);
|
|
39
40
|
const recommendationsResults = useFetchRecommendationPod(cioClient, recommendationsSections);
|
|
40
41
|
const sectionResults = { ...autocompleteResults, ...recommendationsResults };
|
|
41
42
|
const activeSectionsWithData = [];
|
|
42
43
|
activeSections?.forEach((config) => {
|
|
43
|
-
const { identifier
|
|
44
|
-
|
|
45
|
-
if (
|
|
44
|
+
const { identifier } = config;
|
|
45
|
+
let data;
|
|
46
|
+
if (isCustomSection(config)) {
|
|
47
|
+
data = config.data;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
data = sectionResults[identifier];
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(data)) {
|
|
46
53
|
activeSectionsWithData.push({ ...config, data });
|
|
47
54
|
}
|
|
48
55
|
});
|
|
@@ -67,7 +74,7 @@ const useCioAutocomplete = (options) => {
|
|
|
67
74
|
openMenu,
|
|
68
75
|
closeMenu,
|
|
69
76
|
getItemProps: (item) => {
|
|
70
|
-
const { index, sectionId } = getItemPosition({ item,
|
|
77
|
+
const { index, sectionId } = getItemPosition({ item, items });
|
|
71
78
|
const sectionItemTestId = `cio-item-${sectionId?.replace(' ', '')}`;
|
|
72
79
|
return {
|
|
73
80
|
...downshift.getItemProps({ item, index }),
|
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import useDebounce from './useDebounce';
|
|
3
|
+
const transformResponse = (response, options) => {
|
|
4
|
+
const { numTermsWithGroupSuggestions, numGroupsSuggestedPerTerm } = options;
|
|
5
|
+
const newSectionsData = {};
|
|
6
|
+
Object.keys(response.sections).forEach((section) => {
|
|
7
|
+
newSectionsData[section] = [];
|
|
8
|
+
const sectionItems = response.sections[section].map((item) => ({
|
|
9
|
+
...item,
|
|
10
|
+
id: item?.data?.id,
|
|
11
|
+
section,
|
|
12
|
+
}));
|
|
13
|
+
sectionItems.forEach((item, itemIndex) => {
|
|
14
|
+
newSectionsData[section]?.push(item);
|
|
15
|
+
if (section === 'Search Suggestions' &&
|
|
16
|
+
item?.data?.groups?.length &&
|
|
17
|
+
itemIndex < numTermsWithGroupSuggestions) {
|
|
18
|
+
item.data.groups.forEach((group, groupIndex) => {
|
|
19
|
+
if (groupIndex < numGroupsSuggestedPerTerm) {
|
|
20
|
+
const inGroupSearchSuggestion = {
|
|
21
|
+
...item,
|
|
22
|
+
id: `${item.data?.id}_${group.group_id}`,
|
|
23
|
+
groupName: group.display_name,
|
|
24
|
+
groupId: group.group_id,
|
|
25
|
+
};
|
|
26
|
+
newSectionsData[section]?.push(inGroupSearchSuggestion);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
return newSectionsData;
|
|
33
|
+
};
|
|
3
34
|
const autocompleteParameters = {
|
|
4
35
|
resultsPerSection: {},
|
|
5
36
|
// numResults: 8,
|
|
@@ -7,9 +38,10 @@ const autocompleteParameters = {
|
|
|
7
38
|
// filters: {},
|
|
8
39
|
// variationsMap: {}
|
|
9
40
|
};
|
|
10
|
-
const useDebouncedFetchSection = (query, cioClient, autocompleteSections) => {
|
|
41
|
+
const useDebouncedFetchSection = (query, cioClient, autocompleteSections, advancedParameters) => {
|
|
11
42
|
const [sectionsData, setSectionsData] = useState({});
|
|
12
43
|
const debouncedSearchTerm = useDebounce(query);
|
|
44
|
+
const { numTermsWithGroupSuggestions = 0, numGroupsSuggestedPerTerm = 0 } = advancedParameters;
|
|
13
45
|
if (autocompleteSections) {
|
|
14
46
|
autocompleteParameters.resultsPerSection = autocompleteSections.reduce((acc, sectionConfig) => ({
|
|
15
47
|
...acc,
|
|
@@ -21,12 +53,9 @@ const useDebouncedFetchSection = (query, cioClient, autocompleteSections) => {
|
|
|
21
53
|
cioClient?.autocomplete
|
|
22
54
|
.getAutocompleteResults(debouncedSearchTerm, autocompleteParameters)
|
|
23
55
|
.then((response) => {
|
|
24
|
-
const newSectionsData = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
...item,
|
|
28
|
-
section,
|
|
29
|
-
}));
|
|
56
|
+
const newSectionsData = transformResponse(response, {
|
|
57
|
+
numTermsWithGroupSuggestions,
|
|
58
|
+
numGroupsSuggestedPerTerm,
|
|
30
59
|
});
|
|
31
60
|
setSectionsData(newSectionsData);
|
|
32
61
|
});
|
|
@@ -34,7 +63,7 @@ const useDebouncedFetchSection = (query, cioClient, autocompleteSections) => {
|
|
|
34
63
|
else if (!debouncedSearchTerm) {
|
|
35
64
|
setSectionsData({});
|
|
36
65
|
}
|
|
37
|
-
}, [debouncedSearchTerm, cioClient]);
|
|
66
|
+
}, [debouncedSearchTerm, cioClient, numTermsWithGroupSuggestions, numGroupsSuggestedPerTerm]);
|
|
38
67
|
return sectionsData;
|
|
39
68
|
};
|
|
40
69
|
export default useDebouncedFetchSection;
|
package/lib/mjs/typeGuards.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
// Type Guard
|
|
2
|
-
// eslint-disable-next-line
|
|
3
1
|
export function isProduct(item) {
|
|
4
|
-
return item.data
|
|
2
|
+
return item.data?.image_url !== undefined;
|
|
3
|
+
}
|
|
4
|
+
export function isInGroupSuggestion(item) {
|
|
5
|
+
return item.groupName !== undefined;
|
|
6
|
+
}
|
|
7
|
+
export function isCustomSection(config) {
|
|
8
|
+
return config.data !== undefined;
|
|
5
9
|
}
|
package/lib/mjs/utils.js
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
export const getItemPosition = ({ item,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const indexInSection = section.data?.findIndex(({ data: result }) => {
|
|
7
|
-
indexAcrossSections += 1;
|
|
8
|
-
return result?.id === item?.data?.id;
|
|
9
|
-
});
|
|
10
|
-
if (indexInSection === -1) {
|
|
11
|
-
return false; // continue searching through sections
|
|
12
|
-
}
|
|
13
|
-
sectionId = section.identifier; // item found in current section
|
|
14
|
-
return true; // stop looping through sections
|
|
15
|
-
});
|
|
16
|
-
return { sectionId, index: indexAcrossSections };
|
|
2
|
+
export const getItemPosition = ({ item, items }) => {
|
|
3
|
+
const index = items.findIndex((itemInFlatList) => itemInFlatList?.id === item?.id);
|
|
4
|
+
const sectionId = items[index]?.section;
|
|
5
|
+
return { sectionId, index };
|
|
17
6
|
};
|
|
18
7
|
export const camelToStartCase = (camelCaseString) => camelCaseString
|
|
19
8
|
// insert a space before all caps
|
package/lib/styles.css
CHANGED
package/lib/types/constants.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutocompleteSubmitEvent } from './types';
|
|
2
2
|
export declare const apiKey = "key_Gep3oQOu5IMcNh9A";
|
|
3
3
|
export declare const componentDescription = "- import `CioAutocomplete` to render in your JSX.\n- This component handles state management, data fetching, and rendering logic.\n- To use this component, an `apiKey` or `cioJsClient` are required, and an `onSubmit` callback must be passed. All other values are optional.\n- Use different props to configure the behavior of this component.\n- The following stories shows how different props affect the component's behavior\n\n> Note: when we say `cioJsClient`, we are referring to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)\n";
|
|
4
|
-
export declare const hookDescription = "- import `useCioAutocomplete` and call this custom hook in a functional component.\n- This hook leaves rendering logic up to you, while handling:\n - state management\n - data fetching\n - keyboard navigation\n - mouse interactions\n - focus and submit event handling\n- To use this hook, an `apiKey` or `cioJsClient` are required, and an `onSubmit` callback must be passed to the `useCioAutocomplete` hook to configure behavior. All other values are optional.\n- use the <a href=\"https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters\" target=\"__blank\">prop getters</a> and other variables returned by this hook (below) to leverage the functionality described above with jsx elements in your react component definitions\n\nCalling the `useCioAutocomplete` hook returns an object with the following keys:\n\n```jsx\nconst {\n // must be used for a hooks integrations\n query: string, // current input field value\n sections: [{...}], // array of sections data to render in menu list\n getFormProps: () => ({...})), // prop getter for jsx form element\n getInputProps: () => ({...})), // prop getter for jsx input element\n getMenuProps: () => ({...})), // prop getter for jsx element
|
|
4
|
+
export declare const hookDescription = "- import `useCioAutocomplete` and call this custom hook in a functional component.\n- This hook leaves rendering logic up to you, while handling:\n - state management\n - data fetching\n - keyboard navigation\n - mouse interactions\n - focus and submit event handling\n- To use this hook, an `apiKey` or `cioJsClient` are required, and an `onSubmit` callback must be passed to the `useCioAutocomplete` hook to configure behavior. All other values are optional.\n- use the <a href=\"https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters\" target=\"__blank\">prop getters</a> and other variables returned by this hook (below) to leverage the functionality described above with jsx elements in your react component definitions\n\nCalling the `useCioAutocomplete` hook returns an object with the following keys:\n\n```jsx\nconst {\n // must be used for a hooks integrations\n query: string, // current input field value\n sections: [{...}], // array of sections data to render in menu list\n getFormProps: () => ({...})), // prop getter for jsx form element\n getInputProps: () => ({...})), // prop getter for jsx input element\n getMenuProps: () => ({...})), // prop getter for jsx element rendering the results container\n getItemProps: (item) => ({...})), // prop getter for jsx element rendering each result\n\n // available for use, but not required for all use cases\n selectedItem: item, // undefined or current selected item (via hover or arrow keys)\n isOpen: boolean, // current state of the menu list\n openMenu: () => void, // open menu\n closeMenu: () => void, // close menu\n setQuery: () => void, // update the current input field value\n getLabelProps: () => ({...})), // prop getter for a jsx label element\n cioJsClient, // instance of constructorio-client-javascript\n } = useCioAutocomplete(args);\n```\n\n> Note: when we say `cioJsClient`, we are referring to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)\n\nThe following stories show how different options affect the hook's behavior!\n";
|
|
5
5
|
export declare const sectionsDescription = "- by default, typing a query will fetch data for search suggestions and Products\n- to override this, pass an array of sections objects\n- the order of the objects in the `sections` array determines the order of the results\n- each section object must have an `identifier`\n- each section object can specify a `type`\n- each section object can override the default `numResults` of 8\n\nWhen no values are passed for the `sections` argument, the following defaults are used:\n\n```jsx\n[\n {\n identifier: 'Search Suggestions',\n type: 'autocomplete',\n numResults: 8\n },\n {\n identifier: 'Products',\n type: 'autocomplete',\n numResults: 8\n }\n]\n```\n";
|
|
6
6
|
export declare const userEventsDescription = "- pass callback functions to respond to user events\n- if provided, the onFocus callback function will be called each time the user focuses on the text input field\n- if provided, the onChange callback function will be called each time the user changes the value in the text input field\n- the onSubmit callback function will be called each time the user submits the form\n- the user can submit the form by pressing the enter key in the text input field, clicking a submit button within the form, clicking on a result, or pressing enter while a result is selected\n\n> \u26A0\uFE0F NOTE \u26A0\uFE0F Use the Storybook Canvas Actions tab to explore the behavior of all of these `OnEvent` callback functions as you interact with our Default User Events example rendered in the Canvas. In the stories below, Storybook Canvas Actions have been disabled to focus on each of these callback functions in isolation. Each of the example callback functions in the stories below log output to the console tab of the browser's developer tools.";
|
|
7
7
|
export declare const zeroStateDescription = "- when the text input field has no text, we call this zero state\n- by default, the autocomplete shows nothing in the menu it's for zero state\n- to show zero state results, pass an array of section objects for `zeroStateSections`\n- when `zeroStateSections` has sections, the menu will open on user focus by default\n- set `openOnFocus` to false, to only show `zeroStateSections` after user has typed and then cleared the text input, instead of as soon as the user focuses on the text input\n- the order of the objects in the `zeroStateSections` array determines the order of the results\n- each section object must have an `identifier`\n- each section object can specify a `type`\n- each section object can override the default `numResults` of 8";
|
|
@@ -23,3 +23,6 @@ export declare const zeroStateSectionsDescription = "Use `zeroStateSections` to
|
|
|
23
23
|
export declare const openOnFocusDescription = "Use `openOnFocus: false` to show suggestions after a user clears their query, but not when they initially apply focus to the search input field";
|
|
24
24
|
export declare const multipleSectionsDescription = "Use as many different `recommendations` and `custom` sections as you'd like and in whatever order you would like!";
|
|
25
25
|
export declare const customStylesDescription = "\nBy default, importing react components or hooks from this library does not pull any css into your project.\n\nIf you wish to use some starter styles from this library, add an import statement similar to the example import statement below:\n\n`\nimport '@constructor-io/constructorio-ui-autocomplete/styles.css';\n`\n\n<i></i>\n\n- To opt out of all default styling, do not import the `styles.css` stylesheet.\n- The path and syntax in the example above may change depending on your module bundling strategy\n- These starter styles can be used as a foundation to build on top of, or just as a reference for you to replace completely.\n- All starter styles in this library are scoped within the `.cio-autocomplete` css selector.\n- These starter styles are intended to be extended by layering in your own css rules\n- If you like, you can override the container's className like so:\n`autocompleteClassName='custom-autocomplete-container'`\n- If you like, you can pass additional className(s) of your choosing like so:\n`autocompleteClassName='cio-autocomplete custom-autocomplete-container'`\n\n\n```css\n/* Custom Style Sheet */\n.cio-autocomplete.custom-autocomplete-styles form {\n height: 44px;\n width: 600px;\n border-radius: 8px;\n background-color: rgb(247, 247, 247);\n}\n\n.cio-autocomplete.custom-autocomplete-styles .cio-input {\n font-weight: bold;\n}\n\n.cio-autocomplete.custom-autocomplete-styles .cio-form button {\n width: 44px;\n}\n\n.cio-autocomplete.custom-autocomplete-styles .cio-clear-btn {\n right: 24px;\n}\n\n.cio-autocomplete.custom-autocomplete-styles .cio-sectionName {\n margin: 5px 3px;\n}\n\n.cio-autocomplete.custom-autocomplete-styles .cio-results {\n width: 620px;\n max-height: 334px;\n overflow: hidden;\n border-radius: 0px 0px 8px 8px;\n color: rgb(51, 51, 51);\n}\n\n.cio-autocomplete.custom-autocomplete-styles .Products p {\n padding: 5px 5px 0;\n}\n```\n";
|
|
26
|
+
export declare const advancedParametersDescription = "Pass an `advancedParameters` object to configure more options for the data included in the results requested from constructor's servers. The stories below show examples of the optional fields within this `advancedParameters` object that can be used to fine-tune the autosuggest data returned from constructor's servers.";
|
|
27
|
+
export declare const advancedParametersDefaultDescription = "Passing an `advancedParameters` object is optional. Passing an empty object for the `advancedParameters` field behaves the same as not passing an `advancedParameters` object at all. This has no impact on the results returned by default.";
|
|
28
|
+
export declare const termsWithGroupSuggestionsDescription = "Pass integers for the `numTermsWithGroupSuggestions` and `numGroupsSuggestedPerTerm` fields to add suggested group filters to search term suggestions. Not all suggested search terms will have group filters, so these integers are upper limits, used to specify the maximum number of terms with group filters and the maximum number of suggested group filters per term. To see this in action:\n- type \"bab\" in the example below. Notice how the user is presented with a search term of \"baby\" as well as \"baby in Bodysuits\" and \"baby in Baby Organic Cotton\"\n- navigate to the \"Terms With Group Suggestions\" story (using the navigation menu to the left), then use the Controls to adjust the values of `numTermsWithGroupSuggestions` to `3` and `numGroupsSuggestedPerTerm` to `1`. Next, type \"dan\" in the example autocomplete input field. Notice how the user is presented with three different search terms that have a maximum of one \"in {group}\" suggestion each";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CioAutocompleteProps, Item, Section } from '../types';
|
|
2
|
-
export declare const defaultSections:
|
|
1
|
+
import { CioAutocompleteProps, Item, Section, UserDefinedSection } from '../types';
|
|
2
|
+
export declare const defaultSections: UserDefinedSection[];
|
|
3
3
|
export type UseCioAutocompleteOptions = Omit<CioAutocompleteProps, 'children'>;
|
|
4
4
|
declare const useCioAutocomplete: (options: UseCioAutocompleteOptions) => {
|
|
5
5
|
query: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
2
|
import { Nullable } from '@constructor-io/constructorio-client-javascript/lib/types/types';
|
|
3
|
-
import { AutocompleteResultSections,
|
|
4
|
-
declare const useDebouncedFetchSection: (query: string, cioClient: Nullable<ConstructorIOClient>, autocompleteSections
|
|
3
|
+
import { AutocompleteResultSections, UserDefinedSection, AdvancedParameters } from '../types';
|
|
4
|
+
declare const useDebouncedFetchSection: (query: string, cioClient: Nullable<ConstructorIOClient>, autocompleteSections: UserDefinedSection[], advancedParameters: AdvancedParameters) => AutocompleteResultSections;
|
|
5
5
|
export default useDebouncedFetchSection;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import { Item, Product } from './types';
|
|
1
|
+
import { CustomSection, InGroupSuggestion, Item, Product, UserDefinedSection } from './types';
|
|
2
2
|
export declare function isProduct(item: Item): item is Product;
|
|
3
|
+
export declare function isInGroupSuggestion(item: Item): item is InGroupSuggestion;
|
|
4
|
+
export declare function isCustomSection(config: UserDefinedSection): config is CustomSection;
|
package/lib/types/types.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ export type CioClientConfig = {
|
|
|
5
5
|
apiKey?: string;
|
|
6
6
|
cioJsClient?: ConstructorIOClient;
|
|
7
7
|
};
|
|
8
|
+
export type AdvancedParameters = {
|
|
9
|
+
numTermsWithGroupSuggestions?: number;
|
|
10
|
+
numGroupsSuggestedPerTerm?: number;
|
|
11
|
+
};
|
|
8
12
|
export type CioAutocompleteProps = CioClientConfig & {
|
|
9
13
|
openOnFocus?: boolean;
|
|
10
14
|
onSubmit: OnSubmit;
|
|
@@ -12,9 +16,10 @@ export type CioAutocompleteProps = CioClientConfig & {
|
|
|
12
16
|
onChange?: () => void;
|
|
13
17
|
placeholder?: string;
|
|
14
18
|
children?: ReactNode;
|
|
15
|
-
sections?:
|
|
16
|
-
zeroStateSections?:
|
|
19
|
+
sections?: UserDefinedSection[];
|
|
20
|
+
zeroStateSections?: UserDefinedSection[];
|
|
17
21
|
autocompleteClassName?: string;
|
|
22
|
+
advancedParameters?: AdvancedParameters;
|
|
18
23
|
};
|
|
19
24
|
export type AutocompleteSubmitEvent = {
|
|
20
25
|
item: Item;
|
|
@@ -31,13 +36,13 @@ export type ItemPropsOptions = DownshiftGetItemPropsOptions & {
|
|
|
31
36
|
};
|
|
32
37
|
export type GetItemProps = (options: ItemPropsOptions) => object;
|
|
33
38
|
export interface ItemBase extends Record<string, any> {
|
|
34
|
-
id
|
|
39
|
+
id: string;
|
|
40
|
+
section: string;
|
|
35
41
|
url?: string;
|
|
36
42
|
value?: string;
|
|
37
|
-
section: string;
|
|
38
43
|
data?: Record<string, any>;
|
|
39
44
|
}
|
|
40
|
-
export type Item = Product | SearchSuggestion | ItemBase;
|
|
45
|
+
export type Item = Product | SearchSuggestion | InGroupSuggestion | ItemBase;
|
|
41
46
|
export type GetAutocompleteResultsOptions = {
|
|
42
47
|
[sectionIdentifier: string]: {
|
|
43
48
|
numResults: number;
|
|
@@ -56,11 +61,11 @@ export type SectionConfiguration = {
|
|
|
56
61
|
};
|
|
57
62
|
export interface AutocompleteSection extends SectionConfiguration {
|
|
58
63
|
type?: 'autocomplete';
|
|
59
|
-
data
|
|
64
|
+
data: Item[];
|
|
60
65
|
}
|
|
61
66
|
export interface RecommendationsSection extends SectionConfiguration {
|
|
62
67
|
type: 'recommendations';
|
|
63
|
-
data
|
|
68
|
+
data: Item[];
|
|
64
69
|
itemIds?: string[];
|
|
65
70
|
section?: string;
|
|
66
71
|
term?: string;
|
|
@@ -70,7 +75,9 @@ export interface CustomSection extends SectionConfiguration {
|
|
|
70
75
|
data: Item[];
|
|
71
76
|
}
|
|
72
77
|
export type Section = AutocompleteSection | RecommendationsSection | CustomSection;
|
|
73
|
-
export type
|
|
78
|
+
export type UserDefinedSection = CustomSection | SectionConfiguration;
|
|
79
|
+
export type Product = ItemBase & {
|
|
80
|
+
section: 'Products';
|
|
74
81
|
data: {
|
|
75
82
|
facets: {
|
|
76
83
|
name: string;
|
|
@@ -79,27 +86,27 @@ export type Product = {
|
|
|
79
86
|
group_ids: string[];
|
|
80
87
|
id: string;
|
|
81
88
|
image_url: string;
|
|
82
|
-
price: number;
|
|
83
|
-
swatchColor: string;
|
|
84
89
|
url: string;
|
|
85
|
-
variation_id
|
|
90
|
+
variation_id?: string;
|
|
86
91
|
};
|
|
87
92
|
is_slotted: boolean;
|
|
88
93
|
labels: Record<string, unknown>;
|
|
89
94
|
matched_terms: string[];
|
|
90
95
|
value: string;
|
|
91
|
-
section: 'Products';
|
|
92
96
|
};
|
|
93
|
-
export type SearchSuggestion = {
|
|
97
|
+
export type SearchSuggestion = ItemBase & {
|
|
98
|
+
section: 'Search Suggestions';
|
|
94
99
|
data: {
|
|
95
100
|
id: string;
|
|
96
101
|
url?: string;
|
|
97
102
|
};
|
|
98
|
-
id: string;
|
|
99
103
|
is_slotted: boolean;
|
|
100
104
|
labels: Record<string, unknown>;
|
|
101
105
|
matched_terms: string[];
|
|
102
106
|
value: string;
|
|
103
|
-
|
|
107
|
+
};
|
|
108
|
+
export type InGroupSuggestion = SearchSuggestion & {
|
|
109
|
+
groupId: string;
|
|
110
|
+
groupName: string;
|
|
104
111
|
};
|
|
105
112
|
export {};
|
package/lib/types/utils.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
import { OnSubmit,
|
|
2
|
+
import { OnSubmit, Item } from './types';
|
|
3
3
|
export type GetItemPosition = (args: {
|
|
4
4
|
item: Item;
|
|
5
|
-
|
|
5
|
+
items: Item[];
|
|
6
6
|
}) => {
|
|
7
7
|
index: number;
|
|
8
8
|
sectionId: string;
|
package/package.json
CHANGED