@constructor-io/constructorio-ui-autocomplete 1.6.0 → 1.7.1
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 +40 -21
- package/dist/constructorio-ui-autocomplete-bundled.js +43 -0
- package/lib/cjs/hooks/useCioAutocomplete.js +20 -45
- package/lib/cjs/hooks/useConsoleErrors.js +16 -0
- package/lib/cjs/hooks/useDebouncedFetchSections.js +18 -12
- package/lib/cjs/hooks/useDownShift.js +1 -8
- package/lib/cjs/hooks/useFetchRecommendationPod.js +6 -1
- package/lib/cjs/hooks/useItems.js +12 -0
- package/lib/cjs/hooks/useSections.js +36 -0
- package/lib/cjs/utils.js +36 -17
- package/lib/mjs/hooks/useCioAutocomplete.js +20 -45
- package/lib/mjs/hooks/useConsoleErrors.js +14 -0
- package/lib/mjs/hooks/useDebouncedFetchSections.js +18 -14
- package/lib/mjs/hooks/useDownShift.js +1 -7
- package/lib/mjs/hooks/useFetchRecommendationPod.js +6 -1
- package/lib/mjs/hooks/useItems.js +10 -0
- package/lib/mjs/hooks/useSections.js +32 -0
- package/lib/mjs/utils.js +34 -15
- package/lib/types/hooks/useCioAutocomplete.d.ts +3 -3
- package/lib/types/hooks/useConsoleErrors.d.ts +2 -0
- package/lib/types/hooks/useDownShift.d.ts +0 -1
- package/lib/types/hooks/useItems.d.ts +3 -0
- package/lib/types/hooks/useSections.d.ts +8 -0
- package/lib/types/types.d.ts +1 -1
- package/lib/types/utils.d.ts +6 -4
- package/package.json +21 -14
|
@@ -5,11 +5,11 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const useCioClient_1 = tslib_1.__importDefault(require("./useCioClient"));
|
|
7
7
|
const useDownShift_1 = tslib_1.__importDefault(require("./useDownShift"));
|
|
8
|
-
const useDebouncedFetchSections_1 = tslib_1.__importDefault(require("./useDebouncedFetchSections"));
|
|
9
|
-
const useFetchRecommendationPod_1 = tslib_1.__importDefault(require("./useFetchRecommendationPod"));
|
|
10
8
|
const usePrevious_1 = tslib_1.__importDefault(require("./usePrevious"));
|
|
11
9
|
const utils_1 = require("../utils");
|
|
12
|
-
const
|
|
10
|
+
const useConsoleErrors_1 = tslib_1.__importDefault(require("./useConsoleErrors"));
|
|
11
|
+
const useSections_1 = tslib_1.__importDefault(require("./useSections"));
|
|
12
|
+
const useItems_1 = tslib_1.__importDefault(require("./useItems"));
|
|
13
13
|
exports.defaultSections = [
|
|
14
14
|
{
|
|
15
15
|
identifier: 'Search Suggestions',
|
|
@@ -21,50 +21,18 @@ exports.defaultSections = [
|
|
|
21
21
|
},
|
|
22
22
|
];
|
|
23
23
|
const useCioAutocomplete = (options) => {
|
|
24
|
-
const
|
|
25
|
-
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = defaultPlaceholder, sections = exports.defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', advancedParameters = {}, } = options;
|
|
24
|
+
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = 'What can we help you find today?', sections = exports.defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', advancedParameters = {}, } = options;
|
|
26
25
|
const [query, setQuery] = (0, react_1.useState)('');
|
|
27
26
|
const previousQuery = (0, usePrevious_1.default)(query);
|
|
28
27
|
const cioClient = (0, useCioClient_1.default)({ apiKey, cioJsClient });
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
activeSections = [];
|
|
35
|
-
}
|
|
36
|
-
if (zeroStateSections && !Array.isArray(zeroStateSections)) {
|
|
37
|
-
// eslint-disable-next-line
|
|
38
|
-
console.error('useCioAutocomplete expects zeroStateSections to reference an array of section configuration objects');
|
|
39
|
-
activeSections = [];
|
|
40
|
-
}
|
|
41
|
-
const autocompleteSections = activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => config.type === 'autocomplete' || !config.type);
|
|
42
|
-
const recommendationsSections = activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => config.type === 'recommendations');
|
|
43
|
-
const autocompleteResults = (0, useDebouncedFetchSections_1.default)(query, cioClient, autocompleteSections, advancedParameters);
|
|
44
|
-
const recommendationsResults = (0, useFetchRecommendationPod_1.default)(cioClient, recommendationsSections);
|
|
45
|
-
const sectionResults = Object.assign(Object.assign({}, autocompleteResults), recommendationsResults);
|
|
46
|
-
const activeSectionsWithData = [];
|
|
47
|
-
activeSections === null || activeSections === void 0 ? void 0 : activeSections.forEach((config) => {
|
|
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)) {
|
|
57
|
-
activeSectionsWithData.push(Object.assign(Object.assign({}, config), { data }));
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
const items = [];
|
|
61
|
-
activeSectionsWithData === null || activeSectionsWithData === void 0 ? void 0 : activeSectionsWithData.forEach((config) => {
|
|
62
|
-
if (config === null || config === void 0 ? void 0 : config.data) {
|
|
63
|
-
items.push(...config.data);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
const downshift = (0, useDownShift_1.default)({ setQuery, onChange, items, onSubmit, cioClient, previousQuery });
|
|
28
|
+
// Get autocomplete sections (autocomplete + recommendations + custom)
|
|
29
|
+
const { activeSections, activeSectionsWithData, zeroStateActiveSections } = (0, useSections_1.default)(query, cioClient, sections, zeroStateSections, advancedParameters);
|
|
30
|
+
// Get dropdown items array from active sections (autocomplete + recommendations + custom)
|
|
31
|
+
const items = (0, useItems_1.default)(activeSectionsWithData);
|
|
32
|
+
const downshift = (0, useDownShift_1.default)({ setQuery, items, onSubmit, cioClient, previousQuery });
|
|
67
33
|
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex } = downshift;
|
|
34
|
+
// Log console errors
|
|
35
|
+
(0, useConsoleErrors_1.default)(sections, activeSections);
|
|
68
36
|
return {
|
|
69
37
|
query,
|
|
70
38
|
sections: activeSectionsWithData,
|
|
@@ -78,12 +46,19 @@ const useCioAutocomplete = (options) => {
|
|
|
78
46
|
const sectionItemTestId = `cio-item-${sectionId === null || sectionId === void 0 ? void 0 : sectionId.replace(' ', '')}`;
|
|
79
47
|
return Object.assign(Object.assign({}, downshift.getItemProps({ item, index })), { className: `cio-item ${sectionItemTestId}`, 'data-testid': sectionItemTestId });
|
|
80
48
|
},
|
|
81
|
-
getInputProps: () => (Object.assign(Object.assign({}, downshift.getInputProps(
|
|
49
|
+
getInputProps: () => (Object.assign(Object.assign({}, downshift.getInputProps({
|
|
50
|
+
onChange: (e) => {
|
|
51
|
+
setQuery(e.target.value);
|
|
52
|
+
if (onChange) {
|
|
53
|
+
onChange(e.target.value);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
})), { value: query, onFocus: () => {
|
|
82
57
|
var _a;
|
|
83
58
|
if (options.onFocus) {
|
|
84
59
|
options.onFocus();
|
|
85
60
|
}
|
|
86
|
-
if (
|
|
61
|
+
if (zeroStateActiveSections && openOnFocus !== false) {
|
|
87
62
|
downshift.openMenu();
|
|
88
63
|
}
|
|
89
64
|
if (query === null || query === void 0 ? void 0 : query.length) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
const useConsoleErrors = (sections, zeroStateSections) => {
|
|
5
|
+
(0, react_1.useEffect)(() => {
|
|
6
|
+
if (sections && !Array.isArray(sections)) {
|
|
7
|
+
// eslint-disable-next-line
|
|
8
|
+
console.error('useCioAutocomplete expects sections to reference an array of section configuration objects');
|
|
9
|
+
}
|
|
10
|
+
if (zeroStateSections && !Array.isArray(zeroStateSections)) {
|
|
11
|
+
// eslint-disable-next-line
|
|
12
|
+
console.error('useCioAutocomplete expects zeroStateSections to reference an array of section configuration objects');
|
|
13
|
+
}
|
|
14
|
+
}, [sections, zeroStateSections]);
|
|
15
|
+
};
|
|
16
|
+
exports.default = useConsoleErrors;
|
|
@@ -45,18 +45,24 @@ const useDebouncedFetchSection = (query, cioClient, autocompleteSections, advanc
|
|
|
45
45
|
autocompleteParameters.resultsPerSection = autocompleteSections.reduce((acc, sectionConfig) => (Object.assign(Object.assign({}, acc), { [sectionConfig.identifier]: (sectionConfig === null || sectionConfig === void 0 ? void 0 : sectionConfig.numResults) || 8 })), {});
|
|
46
46
|
}
|
|
47
47
|
(0, react_1.useEffect)(() => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
if (debouncedSearchTerm.trim()) {
|
|
50
|
+
try {
|
|
51
|
+
const response = yield (cioClient === null || cioClient === void 0 ? void 0 : cioClient.autocomplete.getAutocompleteResults(debouncedSearchTerm, autocompleteParameters));
|
|
52
|
+
const newSectionsData = transformResponse(response, {
|
|
53
|
+
numTermsWithGroupSuggestions,
|
|
54
|
+
numGroupsSuggestedPerTerm,
|
|
55
|
+
});
|
|
56
|
+
setSectionsData(newSectionsData);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.log(error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (!debouncedSearchTerm) {
|
|
63
|
+
setSectionsData({});
|
|
64
|
+
}
|
|
65
|
+
}))();
|
|
60
66
|
}, [debouncedSearchTerm, cioClient, numTermsWithGroupSuggestions, numGroupsSuggestedPerTerm]);
|
|
61
67
|
return sectionsData;
|
|
62
68
|
};
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
3
|
const downshift_1 = require("downshift");
|
|
5
4
|
let idCounter = 0;
|
|
6
|
-
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = ''
|
|
5
|
+
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = '' }) => (0, downshift_1.useCombobox)({
|
|
7
6
|
id: `cio-autocomplete-${idCounter++}`,
|
|
8
7
|
items,
|
|
9
8
|
itemToString: (item) => (item === null || item === void 0 ? void 0 : item.value) || '',
|
|
10
|
-
onInputValueChange: ({ inputValue = '' }) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
11
|
-
setQuery(inputValue);
|
|
12
|
-
if (onChange) {
|
|
13
|
-
onChange(inputValue);
|
|
14
|
-
}
|
|
15
|
-
}),
|
|
16
9
|
onSelectedItemChange({ selectedItem }) {
|
|
17
10
|
var _a;
|
|
18
11
|
if (selectedItem) {
|
|
@@ -22,7 +22,12 @@ const useFetchRecommendationPod = (cioClient, recommendationPods) => {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
|
|
25
|
+
try {
|
|
26
|
+
setRecommendationResults(recommendationPodResults);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.log(error);
|
|
30
|
+
}
|
|
26
31
|
});
|
|
27
32
|
fetchRecommendationResults();
|
|
28
33
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const useItems = (activeSectionsWithData) => {
|
|
4
|
+
const items = [];
|
|
5
|
+
activeSectionsWithData === null || activeSectionsWithData === void 0 ? void 0 : activeSectionsWithData.forEach((config) => {
|
|
6
|
+
if (config === null || config === void 0 ? void 0 : config.data) {
|
|
7
|
+
items.push(...config.data);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
return items;
|
|
11
|
+
};
|
|
12
|
+
exports.default = useItems;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const useDebouncedFetchSections_1 = tslib_1.__importDefault(require("./useDebouncedFetchSections"));
|
|
7
|
+
const useFetchRecommendationPod_1 = tslib_1.__importDefault(require("./useFetchRecommendationPod"));
|
|
8
|
+
function useSections(query, cioClient, sections, zeroStateSections, advancedParameters) {
|
|
9
|
+
const zeroStateActiveSections = !query.length && zeroStateSections;
|
|
10
|
+
const [activeSections, setActiveSections] = (0, react_1.useState)(zeroStateActiveSections ? zeroStateSections : sections);
|
|
11
|
+
const autocompleteSections = activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => config.type === 'autocomplete' || !config.type);
|
|
12
|
+
const recommendationsSections = activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => config.type === 'recommendations');
|
|
13
|
+
// Fetch Autocomplete Results
|
|
14
|
+
const autocompleteResults = (0, useDebouncedFetchSections_1.default)(query, cioClient, autocompleteSections, advancedParameters);
|
|
15
|
+
// Fetch Recommendations Results
|
|
16
|
+
const recommendationsResults = (0, useFetchRecommendationPod_1.default)(cioClient, recommendationsSections);
|
|
17
|
+
const sectionResults = Object.assign(Object.assign({}, autocompleteResults), recommendationsResults);
|
|
18
|
+
const activeSectionsWithData = (0, utils_1.getActiveSectionsWithData)(activeSections, sectionResults);
|
|
19
|
+
(0, react_1.useEffect)(() => {
|
|
20
|
+
setActiveSections(zeroStateActiveSections ? zeroStateSections : sections);
|
|
21
|
+
}, [query, sections, zeroStateSections, zeroStateActiveSections]);
|
|
22
|
+
(0, react_1.useEffect)(() => {
|
|
23
|
+
if (sections && !Array.isArray(sections)) {
|
|
24
|
+
setActiveSections([]);
|
|
25
|
+
}
|
|
26
|
+
if (zeroStateSections && !Array.isArray(zeroStateSections)) {
|
|
27
|
+
setActiveSections([]);
|
|
28
|
+
}
|
|
29
|
+
}, [sections, zeroStateSections]);
|
|
30
|
+
return {
|
|
31
|
+
activeSections,
|
|
32
|
+
activeSectionsWithData,
|
|
33
|
+
zeroStateActiveSections,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.default = useSections;
|
package/lib/cjs/utils.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getActiveSectionsWithData = exports.getCioClient = exports.disableStoryActions = exports.stringifyWithDefaults = exports.functionStrings = 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 typeGuards_1 = require("./typeGuards");
|
|
6
7
|
const getItemPosition = ({ item, items }) => {
|
|
7
8
|
var _a;
|
|
8
9
|
const index = items.findIndex((itemInFlatList) => (itemInFlatList === null || itemInFlatList === void 0 ? void 0 : itemInFlatList.id) === (item === null || item === void 0 ? void 0 : item.id));
|
|
@@ -57,23 +58,23 @@ ${templateCode}
|
|
|
57
58
|
};
|
|
58
59
|
};
|
|
59
60
|
exports.getStoryParams = getStoryParams;
|
|
60
|
-
exports.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
exports.defaultArgumentsCode = defaultArgumentsCode;
|
|
61
|
+
exports.functionStrings = {
|
|
62
|
+
onSubmit: `(submitEvent) => console.dir(submitEvent)`,
|
|
63
|
+
};
|
|
64
64
|
const stringifyWithDefaults = (obj) => {
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
// Stringify non-function values normally. Add a template block for functions to be replaced later
|
|
66
|
+
let res = JSON.stringify(obj, (key, value) => (value instanceof Function ? `${key}_CODE` : value), ' ');
|
|
67
|
+
// Replace template blocks with function strings
|
|
68
|
+
Array.from(res.matchAll(/"(\w*)_CODE"/g)).forEach((match) => {
|
|
69
|
+
const [codePlaceholder, key] = match;
|
|
70
|
+
const functionString = exports.functionStrings[key];
|
|
71
|
+
if (functionString) {
|
|
72
|
+
res = res.replaceAll(codePlaceholder, functionString);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.error(`Function string for ${key} not found.`); // eslint-disable-line
|
|
76
|
+
}
|
|
77
|
+
});
|
|
77
78
|
return res;
|
|
78
79
|
};
|
|
79
80
|
exports.stringifyWithDefaults = stringifyWithDefaults;
|
|
@@ -92,3 +93,21 @@ const getCioClient = (apiKey) => {
|
|
|
92
93
|
return null;
|
|
93
94
|
};
|
|
94
95
|
exports.getCioClient = getCioClient;
|
|
96
|
+
const getActiveSectionsWithData = (activeSections, sectionResults) => {
|
|
97
|
+
const activeSectionsWithData = [];
|
|
98
|
+
activeSections === null || activeSections === void 0 ? void 0 : activeSections.forEach((config) => {
|
|
99
|
+
const { identifier } = config;
|
|
100
|
+
let data;
|
|
101
|
+
if ((0, typeGuards_1.isCustomSection)(config)) {
|
|
102
|
+
data = config.data;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
data = sectionResults[identifier];
|
|
106
|
+
}
|
|
107
|
+
if (Array.isArray(data)) {
|
|
108
|
+
activeSectionsWithData.push(Object.assign(Object.assign({}, config), { data }));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return activeSectionsWithData;
|
|
112
|
+
};
|
|
113
|
+
exports.getActiveSectionsWithData = getActiveSectionsWithData;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import useCioClient from './useCioClient';
|
|
3
3
|
import useDownShift from './useDownShift';
|
|
4
|
-
import useDebouncedFetchSection from './useDebouncedFetchSections';
|
|
5
|
-
import useFetchRecommendationPod from './useFetchRecommendationPod';
|
|
6
4
|
import usePrevious from './usePrevious';
|
|
7
5
|
import { getItemPosition } from '../utils';
|
|
8
|
-
import
|
|
6
|
+
import useConsoleErrors from './useConsoleErrors';
|
|
7
|
+
import useSections from './useSections';
|
|
8
|
+
import useItems from './useItems';
|
|
9
9
|
export const defaultSections = [
|
|
10
10
|
{
|
|
11
11
|
identifier: 'Search Suggestions',
|
|
@@ -17,50 +17,18 @@ export const defaultSections = [
|
|
|
17
17
|
},
|
|
18
18
|
];
|
|
19
19
|
const useCioAutocomplete = (options) => {
|
|
20
|
-
const
|
|
21
|
-
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = defaultPlaceholder, sections = defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', advancedParameters = {}, } = options;
|
|
20
|
+
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, placeholder = 'What can we help you find today?', sections = defaultSections, zeroStateSections, autocompleteClassName = 'cio-autocomplete', advancedParameters = {}, } = options;
|
|
22
21
|
const [query, setQuery] = useState('');
|
|
23
22
|
const previousQuery = usePrevious(query);
|
|
24
23
|
const cioClient = useCioClient({ apiKey, cioJsClient });
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
activeSections = [];
|
|
31
|
-
}
|
|
32
|
-
if (zeroStateSections && !Array.isArray(zeroStateSections)) {
|
|
33
|
-
// eslint-disable-next-line
|
|
34
|
-
console.error('useCioAutocomplete expects zeroStateSections to reference an array of section configuration objects');
|
|
35
|
-
activeSections = [];
|
|
36
|
-
}
|
|
37
|
-
const autocompleteSections = activeSections?.filter((config) => config.type === 'autocomplete' || !config.type);
|
|
38
|
-
const recommendationsSections = activeSections?.filter((config) => config.type === 'recommendations');
|
|
39
|
-
const autocompleteResults = useDebouncedFetchSection(query, cioClient, autocompleteSections, advancedParameters);
|
|
40
|
-
const recommendationsResults = useFetchRecommendationPod(cioClient, recommendationsSections);
|
|
41
|
-
const sectionResults = { ...autocompleteResults, ...recommendationsResults };
|
|
42
|
-
const activeSectionsWithData = [];
|
|
43
|
-
activeSections?.forEach((config) => {
|
|
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)) {
|
|
53
|
-
activeSectionsWithData.push({ ...config, data });
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
const items = [];
|
|
57
|
-
activeSectionsWithData?.forEach((config) => {
|
|
58
|
-
if (config?.data) {
|
|
59
|
-
items.push(...config.data);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
const downshift = useDownShift({ setQuery, onChange, items, onSubmit, cioClient, previousQuery });
|
|
24
|
+
// Get autocomplete sections (autocomplete + recommendations + custom)
|
|
25
|
+
const { activeSections, activeSectionsWithData, zeroStateActiveSections } = useSections(query, cioClient, sections, zeroStateSections, advancedParameters);
|
|
26
|
+
// Get dropdown items array from active sections (autocomplete + recommendations + custom)
|
|
27
|
+
const items = useItems(activeSectionsWithData);
|
|
28
|
+
const downshift = useDownShift({ setQuery, items, onSubmit, cioClient, previousQuery });
|
|
63
29
|
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex } = downshift;
|
|
30
|
+
// Log console errors
|
|
31
|
+
useConsoleErrors(sections, activeSections);
|
|
64
32
|
return {
|
|
65
33
|
query,
|
|
66
34
|
sections: activeSectionsWithData,
|
|
@@ -83,13 +51,20 @@ const useCioAutocomplete = (options) => {
|
|
|
83
51
|
};
|
|
84
52
|
},
|
|
85
53
|
getInputProps: () => ({
|
|
86
|
-
...downshift.getInputProps(
|
|
54
|
+
...downshift.getInputProps({
|
|
55
|
+
onChange: (e) => {
|
|
56
|
+
setQuery(e.target.value);
|
|
57
|
+
if (onChange) {
|
|
58
|
+
onChange(e.target.value);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
87
62
|
value: query,
|
|
88
63
|
onFocus: () => {
|
|
89
64
|
if (options.onFocus) {
|
|
90
65
|
options.onFocus();
|
|
91
66
|
}
|
|
92
|
-
if (
|
|
67
|
+
if (zeroStateActiveSections && openOnFocus !== false) {
|
|
93
68
|
downshift.openMenu();
|
|
94
69
|
}
|
|
95
70
|
if (query?.length) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
const useConsoleErrors = (sections, zeroStateSections) => {
|
|
3
|
+
useEffect(() => {
|
|
4
|
+
if (sections && !Array.isArray(sections)) {
|
|
5
|
+
// eslint-disable-next-line
|
|
6
|
+
console.error('useCioAutocomplete expects sections to reference an array of section configuration objects');
|
|
7
|
+
}
|
|
8
|
+
if (zeroStateSections && !Array.isArray(zeroStateSections)) {
|
|
9
|
+
// eslint-disable-next-line
|
|
10
|
+
console.error('useCioAutocomplete expects zeroStateSections to reference an array of section configuration objects');
|
|
11
|
+
}
|
|
12
|
+
}, [sections, zeroStateSections]);
|
|
13
|
+
};
|
|
14
|
+
export default useConsoleErrors;
|
|
@@ -49,20 +49,24 @@ const useDebouncedFetchSection = (query, cioClient, autocompleteSections, advanc
|
|
|
49
49
|
}), {});
|
|
50
50
|
}
|
|
51
51
|
useEffect(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
(async () => {
|
|
53
|
+
if (debouncedSearchTerm.trim()) {
|
|
54
|
+
try {
|
|
55
|
+
const response = await cioClient?.autocomplete.getAutocompleteResults(debouncedSearchTerm, autocompleteParameters);
|
|
56
|
+
const newSectionsData = transformResponse(response, {
|
|
57
|
+
numTermsWithGroupSuggestions,
|
|
58
|
+
numGroupsSuggestedPerTerm,
|
|
59
|
+
});
|
|
60
|
+
setSectionsData(newSectionsData);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.log(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (!debouncedSearchTerm) {
|
|
67
|
+
setSectionsData({});
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
66
70
|
}, [debouncedSearchTerm, cioClient, numTermsWithGroupSuggestions, numGroupsSuggestedPerTerm]);
|
|
67
71
|
return sectionsData;
|
|
68
72
|
};
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { useCombobox } from 'downshift';
|
|
2
2
|
let idCounter = 0;
|
|
3
|
-
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = ''
|
|
3
|
+
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = '' }) => useCombobox({
|
|
4
4
|
id: `cio-autocomplete-${idCounter++}`,
|
|
5
5
|
items,
|
|
6
6
|
itemToString: (item) => item?.value || '',
|
|
7
|
-
onInputValueChange: async ({ inputValue = '' }) => {
|
|
8
|
-
setQuery(inputValue);
|
|
9
|
-
if (onChange) {
|
|
10
|
-
onChange(inputValue);
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
7
|
onSelectedItemChange({ selectedItem }) {
|
|
14
8
|
if (selectedItem) {
|
|
15
9
|
setQuery(selectedItem.value || '');
|
|
@@ -17,7 +17,12 @@ const useFetchRecommendationPod = (cioClient, recommendationPods) => {
|
|
|
17
17
|
}));
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
|
-
|
|
20
|
+
try {
|
|
21
|
+
setRecommendationResults(recommendationPodResults);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.log(error);
|
|
25
|
+
}
|
|
21
26
|
};
|
|
22
27
|
fetchRecommendationResults();
|
|
23
28
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { getActiveSectionsWithData } from '../utils';
|
|
3
|
+
import useDebouncedFetchSection from './useDebouncedFetchSections';
|
|
4
|
+
import useFetchRecommendationPod from './useFetchRecommendationPod';
|
|
5
|
+
export default function useSections(query, cioClient, sections, zeroStateSections, advancedParameters) {
|
|
6
|
+
const zeroStateActiveSections = !query.length && zeroStateSections;
|
|
7
|
+
const [activeSections, setActiveSections] = useState(zeroStateActiveSections ? zeroStateSections : sections);
|
|
8
|
+
const autocompleteSections = activeSections?.filter((config) => config.type === 'autocomplete' || !config.type);
|
|
9
|
+
const recommendationsSections = activeSections?.filter((config) => config.type === 'recommendations');
|
|
10
|
+
// Fetch Autocomplete Results
|
|
11
|
+
const autocompleteResults = useDebouncedFetchSection(query, cioClient, autocompleteSections, advancedParameters);
|
|
12
|
+
// Fetch Recommendations Results
|
|
13
|
+
const recommendationsResults = useFetchRecommendationPod(cioClient, recommendationsSections);
|
|
14
|
+
const sectionResults = { ...autocompleteResults, ...recommendationsResults };
|
|
15
|
+
const activeSectionsWithData = getActiveSectionsWithData(activeSections, sectionResults);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
setActiveSections(zeroStateActiveSections ? zeroStateSections : sections);
|
|
18
|
+
}, [query, sections, zeroStateSections, zeroStateActiveSections]);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (sections && !Array.isArray(sections)) {
|
|
21
|
+
setActiveSections([]);
|
|
22
|
+
}
|
|
23
|
+
if (zeroStateSections && !Array.isArray(zeroStateSections)) {
|
|
24
|
+
setActiveSections([]);
|
|
25
|
+
}
|
|
26
|
+
}, [sections, zeroStateSections]);
|
|
27
|
+
return {
|
|
28
|
+
activeSections,
|
|
29
|
+
activeSectionsWithData,
|
|
30
|
+
zeroStateActiveSections,
|
|
31
|
+
};
|
|
32
|
+
}
|
package/lib/mjs/utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
|
+
import { isCustomSection } from './typeGuards';
|
|
2
3
|
export const getItemPosition = ({ item, items }) => {
|
|
3
4
|
const index = items.findIndex((itemInFlatList) => itemInFlatList?.id === item?.id);
|
|
4
5
|
const sectionId = items[index]?.section;
|
|
@@ -44,22 +45,23 @@ ${templateCode}
|
|
|
44
45
|
},
|
|
45
46
|
};
|
|
46
47
|
};
|
|
47
|
-
export const
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
export const functionStrings = {
|
|
49
|
+
onSubmit: `(submitEvent) => console.dir(submitEvent)`,
|
|
50
|
+
};
|
|
50
51
|
export const stringifyWithDefaults = (obj) => {
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
// Stringify non-function values normally. Add a template block for functions to be replaced later
|
|
53
|
+
let res = JSON.stringify(obj, (key, value) => (value instanceof Function ? `${key}_CODE` : value), ' ');
|
|
54
|
+
// Replace template blocks with function strings
|
|
55
|
+
Array.from(res.matchAll(/"(\w*)_CODE"/g)).forEach((match) => {
|
|
56
|
+
const [codePlaceholder, key] = match;
|
|
57
|
+
const functionString = functionStrings[key];
|
|
58
|
+
if (functionString) {
|
|
59
|
+
res = res.replaceAll(codePlaceholder, functionString);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.error(`Function string for ${key} not found.`); // eslint-disable-line
|
|
63
|
+
}
|
|
64
|
+
});
|
|
63
65
|
return res;
|
|
64
66
|
};
|
|
65
67
|
export const disableStoryActions = (story) => {
|
|
@@ -75,3 +77,20 @@ export const getCioClient = (apiKey) => {
|
|
|
75
77
|
}
|
|
76
78
|
return null;
|
|
77
79
|
};
|
|
80
|
+
export const getActiveSectionsWithData = (activeSections, sectionResults) => {
|
|
81
|
+
const activeSectionsWithData = [];
|
|
82
|
+
activeSections?.forEach((config) => {
|
|
83
|
+
const { identifier } = config;
|
|
84
|
+
let data;
|
|
85
|
+
if (isCustomSection(config)) {
|
|
86
|
+
data = config.data;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
data = sectionResults[identifier];
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(data)) {
|
|
92
|
+
activeSectionsWithData.push({ ...config, data });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return activeSectionsWithData;
|
|
96
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { CioAutocompleteProps,
|
|
1
|
+
import { CioAutocompleteProps, UserDefinedSection } from '../types';
|
|
2
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;
|
|
6
|
-
sections: Section[];
|
|
6
|
+
sections: import("../types").Section[];
|
|
7
7
|
isOpen: boolean;
|
|
8
8
|
getMenuProps: () => any;
|
|
9
9
|
getLabelProps: (options?: import("downshift").UseComboboxGetLabelPropsOptions | undefined) => any;
|
|
@@ -21,6 +21,6 @@ declare const useCioAutocomplete: (options: UseCioAutocompleteOptions) => {
|
|
|
21
21
|
setQuery: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
22
22
|
cioClient: import("@constructor-io/constructorio-client-javascript/lib/types/types").Nullable<import("@constructor-io/constructorio-client-javascript")>;
|
|
23
23
|
autocompleteClassName: string;
|
|
24
|
-
selectedItem: Item;
|
|
24
|
+
selectedItem: import("../types").Item;
|
|
25
25
|
};
|
|
26
26
|
export default useCioAutocomplete;
|
|
@@ -9,7 +9,6 @@ type UseDownShiftOptions = {
|
|
|
9
9
|
onSubmit: OnSubmit;
|
|
10
10
|
previousQuery?: string;
|
|
11
11
|
cioClient: Nullable<ConstructorIOClient>;
|
|
12
|
-
onChange?: (string: any) => void;
|
|
13
12
|
};
|
|
14
13
|
export type DownShift = UseComboboxReturnValue<Item>;
|
|
15
14
|
type UseDownShift = (options: UseDownShiftOptions) => DownShift;
|