@constructor-io/constructorio-ui-autocomplete 1.23.17 → 1.23.19
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/dist/constructorio-ui-autocomplete-bundled.js +11 -11
- package/lib/cjs/constants.js +2 -1
- package/lib/cjs/hooks/useCioAutocomplete.js +3 -3
- package/lib/cjs/hooks/useDownShift.js +2 -1
- package/lib/cjs/hooks/useSections/index.js +38 -0
- package/lib/cjs/hooks/useSections/useActiveSections.js +25 -0
- package/lib/cjs/hooks/useSections/useActiveSectionsWithData.js +16 -0
- package/lib/cjs/hooks/useSections/useRemoveSections.js +24 -0
- package/lib/cjs/hooks/useSections/useSectionsResults.js +28 -0
- package/lib/cjs/utils.js +8 -4
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/constants.js +1 -0
- package/lib/mjs/hooks/useCioAutocomplete.js +3 -3
- package/lib/mjs/hooks/useDownShift.js +2 -1
- package/lib/mjs/hooks/useSections/index.js +34 -0
- package/lib/mjs/hooks/useSections/useActiveSections.js +22 -0
- package/lib/mjs/hooks/useSections/useActiveSectionsWithData.js +13 -0
- package/lib/mjs/hooks/useSections/useRemoveSections.js +20 -0
- package/lib/mjs/hooks/useSections/useSectionsResults.js +24 -0
- package/lib/mjs/utils.js +8 -4
- package/lib/mjs/version.js +1 -1
- package/lib/types/constants.d.ts +1 -0
- package/lib/types/hooks/useDownShift.d.ts +2 -1
- package/lib/types/hooks/{useSections.d.ts → useSections/index.d.ts} +3 -3
- package/lib/types/hooks/useSections/useActiveSections.d.ts +6 -0
- package/lib/types/hooks/useSections/useActiveSectionsWithData.d.ts +3 -0
- package/lib/types/hooks/useSections/useRemoveSections.d.ts +2 -0
- package/lib/types/hooks/useSections/useSectionsResults.d.ts +15 -0
- package/lib/types/types.d.ts +2 -1
- package/lib/types/utils.d.ts +1 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/lib/cjs/hooks/useSections.js +0 -63
- package/lib/mjs/hooks/useSections.js +0 -58
package/lib/cjs/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.displayShowAllResultsButtonDescription = exports.customRenderItemDescription = exports.translationsDescription = exports.fetchZeroStateOnFocusDescription = exports.debounceDescription = exports.termsWithImagesAndCountsDescription = exports.filteredSuggestionsDescription = 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.displaySearchTermHighlightsDescription = exports.recommendationsDescription = exports.sectionOrderDescription = exports.numResultsDescription = exports.contentDescription = exports.productsDescription = exports.searchSuggestionsDescription = exports.placeholderDescription = exports.cioJsClientOptionsDescription = exports.cioJsClientDescription = exports.apiKeyDescription = exports.fullFeaturedAndStyledExampleDescription = exports.zeroStateDescription = exports.userEventsDescription = exports.sectionsDescription = exports.hookDescription = exports.componentDescription = exports.apiKey = void 0;
|
|
3
|
+
exports.displayShowAllResultsButtonDescription = exports.customRenderItemDescription = exports.translationsDescription = exports.fetchZeroStateOnFocusDescription = exports.debounceDescription = exports.termsWithImagesAndCountsDescription = exports.filteredSuggestionsDescription = exports.termsWithGroupSuggestionsDescription = exports.advancedParametersDefaultDescription = exports.advancedParametersDescription = exports.customStylesDescription = exports.multipleSectionsDescription = exports.openOnFocusDescription = exports.zeroStateSectionsDescription = exports.onSubmitDefault = exports.onSubmitDescription = exports.onIsOpenChangeDescription = exports.onChangeDescription = exports.onFocusDescription = exports.customSectionDescription = exports.displaySearchTermHighlightsDescription = exports.recommendationsDescription = exports.sectionOrderDescription = exports.numResultsDescription = exports.contentDescription = exports.productsDescription = exports.searchSuggestionsDescription = exports.placeholderDescription = exports.cioJsClientOptionsDescription = exports.cioJsClientDescription = exports.apiKeyDescription = exports.fullFeaturedAndStyledExampleDescription = exports.zeroStateDescription = exports.userEventsDescription = exports.sectionsDescription = exports.hookDescription = exports.componentDescription = exports.apiKey = void 0;
|
|
4
4
|
// Autocomplete key index
|
|
5
5
|
exports.apiKey = 'key_M57QS8SMPdLdLx4x';
|
|
6
6
|
/// //////////////////////////////
|
|
@@ -129,6 +129,7 @@ exports.displaySearchTermHighlightsDescription = `Use constructor's auto highlig
|
|
|
129
129
|
exports.customSectionDescription = `Use a custom section, by managing and passing your own data, with \`"type": "custom"\` and \`"data":[{...}]\``;
|
|
130
130
|
exports.onFocusDescription = `Pass an \`onFocus\` callback function to execute some code each time the user applies focus to the text input field`;
|
|
131
131
|
exports.onChangeDescription = `Pass an \`onChange\` callback function to execute some code each time the user changes the value of the text input field`;
|
|
132
|
+
exports.onIsOpenChangeDescription = `Pass an \`onIsOpenChange\` callback function to execute some code each time the dropdown state changes ie: close/open`;
|
|
132
133
|
exports.onSubmitDescription = `Pass an \`onSubmit\` callback function to execute some code after a user submits the search form.
|
|
133
134
|
|
|
134
135
|
Your callback function will be invoked with a submit event containing useful metadata about the submit event:
|
|
@@ -41,7 +41,7 @@ const convertLegacyParametersAndAddDefaults = (sections) => sections.map((config
|
|
|
41
41
|
const useCioAutocomplete = (options) => {
|
|
42
42
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
43
|
const memoizedOptions = (0, react_1.useMemo)(() => options, [JSON.stringify(options)]);
|
|
44
|
-
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, cioJsClientOptions, placeholder = 'What can we help you find today?', autocompleteClassName = 'cio-autocomplete', advancedParameters, defaultInput, getSearchResultsUrl, } = memoizedOptions;
|
|
44
|
+
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, cioJsClientOptions, placeholder = 'What can we help you find today?', autocompleteClassName = 'cio-autocomplete', advancedParameters, defaultInput, getSearchResultsUrl, onIsOpenChange, } = memoizedOptions;
|
|
45
45
|
let { sections = exports.defaultSections, zeroStateSections } = memoizedOptions;
|
|
46
46
|
sections = (0, react_1.useMemo)(() => {
|
|
47
47
|
if (sections) {
|
|
@@ -63,7 +63,7 @@ const useCioAutocomplete = (options) => {
|
|
|
63
63
|
const features = (0, react_1.useMemo)(() => (0, utils_1.getFeatures)(request), [request]);
|
|
64
64
|
// Get dropdown items array from active sections (autocomplete + recommendations + custom)
|
|
65
65
|
const items = (0, react_1.useMemo)(() => (0, utils_1.getItemsForActiveSections)(activeSectionsWithData), [activeSectionsWithData]);
|
|
66
|
-
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex, getInputProps, getItemProps: getItemPropsDownShift, } = (0, useDownShift_1.default)({ setQuery, items, onSubmit, cioClient, previousQuery });
|
|
66
|
+
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex, getInputProps, getItemProps: getItemPropsDownShift, } = (0, useDownShift_1.default)({ setQuery, items, onSubmit, cioClient, previousQuery, onIsOpenChange });
|
|
67
67
|
// Log console errors
|
|
68
68
|
(0, useConsoleErrors_1.default)(sections, activeSections);
|
|
69
69
|
// Track recommendation view
|
|
@@ -108,7 +108,7 @@ const useCioAutocomplete = (options) => {
|
|
|
108
108
|
openMenu();
|
|
109
109
|
}
|
|
110
110
|
try {
|
|
111
|
-
if (
|
|
111
|
+
if (advancedParameters === null || advancedParameters === void 0 ? void 0 : advancedParameters.fetchZeroStateOnFocus) {
|
|
112
112
|
fetchRecommendationResults();
|
|
113
113
|
}
|
|
114
114
|
(_a = cioClient === null || cioClient === void 0 ? void 0 : cioClient.tracker) === null || _a === void 0 ? void 0 : _a.trackInputFocus();
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const downshift_1 = require("downshift");
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
let idCounter = 0;
|
|
6
|
-
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = '' }) => (0, downshift_1.useCombobox)({
|
|
6
|
+
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = '', onIsOpenChange, }) => (0, downshift_1.useCombobox)({
|
|
7
7
|
id: `cio-autocomplete-${idCounter++}`,
|
|
8
8
|
items,
|
|
9
9
|
itemToString: (item) => (item === null || item === void 0 ? void 0 : item.value) || '',
|
|
@@ -50,5 +50,6 @@ const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = ''
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
|
+
onIsOpenChange,
|
|
53
54
|
});
|
|
54
55
|
exports.default = useDownShift;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
/* eslint-disable max-params */
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const useActiveSections_1 = tslib_1.__importDefault(require("./useActiveSections"));
|
|
7
|
+
const useSectionsResults_1 = tslib_1.__importDefault(require("./useSectionsResults"));
|
|
8
|
+
const useActiveSectionsWithData_1 = tslib_1.__importDefault(require("./useActiveSectionsWithData"));
|
|
9
|
+
const useRemoveSections_1 = tslib_1.__importDefault(require("./useRemoveSections"));
|
|
10
|
+
function useSections(query, cioClient, sections, zeroStateSections, advancedParameters) {
|
|
11
|
+
const [podsData, setPodsData] = (0, react_1.useState)({});
|
|
12
|
+
// Get Active Sections defined by configuration object
|
|
13
|
+
const { activeSections, showZeroStateSections, setActiveSections } = (0, useActiveSections_1.default)(query, sections, podsData, zeroStateSections);
|
|
14
|
+
// create refs for active sections
|
|
15
|
+
const sectionsRefs = (0, react_1.useRef)(activeSections.map(() => (0, react_1.createRef)()));
|
|
16
|
+
// Get API results for each active section
|
|
17
|
+
const { recommendations, autocomplete } = (0, useSectionsResults_1.default)(query, cioClient, activeSections, advancedParameters);
|
|
18
|
+
// Access the Pods Data from the Recommendations response to update Active Sections configuration
|
|
19
|
+
(0, react_1.useEffect)(() => {
|
|
20
|
+
if (!recommendations.podsData) {
|
|
21
|
+
setPodsData(recommendations.podsData);
|
|
22
|
+
}
|
|
23
|
+
}, [recommendations.podsData]);
|
|
24
|
+
(0, useRemoveSections_1.default)(sections, podsData, setActiveSections, showZeroStateSections, zeroStateSections);
|
|
25
|
+
// Combine recommendations and autocomplete results in sectionsResults
|
|
26
|
+
const sectionsResults = (0, react_1.useMemo)(() => (Object.assign(Object.assign({}, autocomplete.results), recommendations.results)), [autocomplete.results, recommendations.results]);
|
|
27
|
+
// Return current active sections populated with data from the API response sectionsResults
|
|
28
|
+
const activeSectionsWithData = (0, useActiveSectionsWithData_1.default)(sectionsResults, activeSections, sectionsRefs, query);
|
|
29
|
+
return {
|
|
30
|
+
fetchRecommendationResults: recommendations.fetchRecommendationResults,
|
|
31
|
+
activeSections,
|
|
32
|
+
activeSectionsWithData,
|
|
33
|
+
zeroStateActiveSections: showZeroStateSections,
|
|
34
|
+
request: autocomplete.request,
|
|
35
|
+
totalNumResultsPerSection: autocomplete.totalNumResultsPerSection,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
exports.default = useSections;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
const typeGuards_1 = require("../../typeGuards");
|
|
5
|
+
function useActiveSections(query, sections, podsData, zeroStateSections) {
|
|
6
|
+
const showZeroStateSections = !query.length && !!(zeroStateSections === null || zeroStateSections === void 0 ? void 0 : zeroStateSections.length);
|
|
7
|
+
const [activeSections, setActiveSections] = (0, react_1.useState)(showZeroStateSections ? zeroStateSections : sections);
|
|
8
|
+
// Merge Recommendation Pods Display Name from Dashboard
|
|
9
|
+
const activeSectionsTransformed = (0, react_1.useMemo)(() => activeSections.map((config) => {
|
|
10
|
+
const mergedConfig = config;
|
|
11
|
+
if ((0, typeGuards_1.isRecommendationsSection)(config)) {
|
|
12
|
+
const podData = podsData === null || podsData === void 0 ? void 0 : podsData[config.podId];
|
|
13
|
+
const libraryDisplayName = config.displayName;
|
|
14
|
+
const dashboardDisplayName = podData === null || podData === void 0 ? void 0 : podData.displayName;
|
|
15
|
+
mergedConfig.displayName = libraryDisplayName || dashboardDisplayName;
|
|
16
|
+
}
|
|
17
|
+
return mergedConfig;
|
|
18
|
+
}), [activeSections, podsData]);
|
|
19
|
+
return {
|
|
20
|
+
activeSections: activeSectionsTransformed,
|
|
21
|
+
showZeroStateSections,
|
|
22
|
+
setActiveSections,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
exports.default = useActiveSections;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
function useActiveSectionsWithData(sectionsResults, activeSections, sectionsRefs, query) {
|
|
6
|
+
const [activeSectionsWithData, setActiveSectionsWithData] = (0, react_1.useState)([]);
|
|
7
|
+
// Add to active sections the results data and refs when autocomplete results or recommendation results fetched
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
const activeSectionsWithDataValue = (0, utils_1.getActiveSectionsWithData)(activeSections, sectionsResults, sectionsRefs);
|
|
10
|
+
if (activeSectionsWithDataValue.length || !query) {
|
|
11
|
+
setActiveSectionsWithData(activeSectionsWithDataValue);
|
|
12
|
+
}
|
|
13
|
+
}, [activeSections, sectionsResults, sectionsRefs, query]);
|
|
14
|
+
return activeSectionsWithData;
|
|
15
|
+
}
|
|
16
|
+
exports.default = useActiveSectionsWithData;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/* eslint-disable max-params */
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const utils_1 = require("../../utils");
|
|
6
|
+
function useRemoveSections(sections, podsData, setActiveSections, showZeroStateSections, zeroStateSections) {
|
|
7
|
+
// Remove sections if necessary
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
const features = (0, utils_1.getFeatures)((_b = (_a = Object.values(podsData || {})) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.request);
|
|
11
|
+
if (showZeroStateSections) {
|
|
12
|
+
if (!features.featureDisplayZeroStateRecommendations) {
|
|
13
|
+
setActiveSections([]);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
setActiveSections(zeroStateSections);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
setActiveSections(sections);
|
|
21
|
+
}
|
|
22
|
+
}, [zeroStateSections, showZeroStateSections, sections, podsData, setActiveSections]);
|
|
23
|
+
}
|
|
24
|
+
exports.default = useRemoveSections;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const useDebouncedFetchSections_1 = tslib_1.__importDefault(require("../useDebouncedFetchSections"));
|
|
6
|
+
const useFetchRecommendationPod_1 = tslib_1.__importDefault(require("../useFetchRecommendationPod"));
|
|
7
|
+
const typeGuards_1 = require("../../typeGuards");
|
|
8
|
+
function useSectionsResults(query, cioClient, activeSections, advancedParameters) {
|
|
9
|
+
// Fetch Autocomplete Results
|
|
10
|
+
const activeAutocompleteSections = (0, react_1.useMemo)(() => activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => (0, typeGuards_1.isAutocompleteSection)(config)), [activeSections]);
|
|
11
|
+
const { sectionsData: autocompleteResults, request, totalNumResultsPerSection, } = (0, useDebouncedFetchSections_1.default)(query, cioClient, activeAutocompleteSections, advancedParameters);
|
|
12
|
+
// Fetch Recommendations Results
|
|
13
|
+
const activeRecommendationsSections = (0, react_1.useMemo)(() => activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => (0, typeGuards_1.isRecommendationsSection)(config)), [activeSections]);
|
|
14
|
+
const { fetchRecommendationResults, recommendationsResults, podsData } = (0, useFetchRecommendationPod_1.default)(cioClient, activeRecommendationsSections, advancedParameters === null || advancedParameters === void 0 ? void 0 : advancedParameters.fetchZeroStateOnFocus);
|
|
15
|
+
return {
|
|
16
|
+
recommendations: {
|
|
17
|
+
results: recommendationsResults,
|
|
18
|
+
fetchRecommendationResults,
|
|
19
|
+
podsData,
|
|
20
|
+
},
|
|
21
|
+
autocomplete: {
|
|
22
|
+
results: autocompleteResults,
|
|
23
|
+
request,
|
|
24
|
+
totalNumResultsPerSection,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
exports.default = useSectionsResults;
|
package/lib/cjs/utils.js
CHANGED
|
@@ -144,14 +144,14 @@ const getCioClient = (apiKey, cioJsClientOptions) => {
|
|
|
144
144
|
return null;
|
|
145
145
|
};
|
|
146
146
|
exports.getCioClient = getCioClient;
|
|
147
|
-
const getActiveSectionsWithData = (activeSections,
|
|
147
|
+
const getActiveSectionsWithData = (activeSections, sectionsResults, sectionsRefs) => {
|
|
148
148
|
const activeSectionsWithData = [];
|
|
149
|
-
|
|
149
|
+
const getSectionData = (sectionConfig) => {
|
|
150
150
|
const { type } = sectionConfig;
|
|
151
151
|
let sectionData;
|
|
152
152
|
switch (type) {
|
|
153
153
|
case 'recommendations':
|
|
154
|
-
sectionData =
|
|
154
|
+
sectionData = sectionsResults[sectionConfig.podId];
|
|
155
155
|
break;
|
|
156
156
|
case 'custom':
|
|
157
157
|
// Copy id from data to the top level
|
|
@@ -162,8 +162,12 @@ const getActiveSectionsWithData = (activeSections, sectionResults, sectionsRefs)
|
|
|
162
162
|
break;
|
|
163
163
|
default:
|
|
164
164
|
// Autocomplete
|
|
165
|
-
sectionData =
|
|
165
|
+
sectionData = sectionsResults[sectionConfig.indexSectionName];
|
|
166
166
|
}
|
|
167
|
+
return sectionData;
|
|
168
|
+
};
|
|
169
|
+
activeSections === null || activeSections === void 0 ? void 0 : activeSections.forEach((sectionConfig, index) => {
|
|
170
|
+
const sectionData = getSectionData(sectionConfig);
|
|
167
171
|
if (Array.isArray(sectionData)) {
|
|
168
172
|
const section = Object.assign(Object.assign({}, sectionConfig), { data: sectionData });
|
|
169
173
|
// If ref passed as part of `SectionConfiguration`, use it.
|
package/lib/cjs/version.js
CHANGED
package/lib/mjs/constants.js
CHANGED
|
@@ -126,6 +126,7 @@ export const displaySearchTermHighlightsDescription = `Use constructor's auto hi
|
|
|
126
126
|
export const customSectionDescription = `Use a custom section, by managing and passing your own data, with \`"type": "custom"\` and \`"data":[{...}]\``;
|
|
127
127
|
export const onFocusDescription = `Pass an \`onFocus\` callback function to execute some code each time the user applies focus to the text input field`;
|
|
128
128
|
export const onChangeDescription = `Pass an \`onChange\` callback function to execute some code each time the user changes the value of the text input field`;
|
|
129
|
+
export const onIsOpenChangeDescription = `Pass an \`onIsOpenChange\` callback function to execute some code each time the dropdown state changes ie: close/open`;
|
|
129
130
|
export const onSubmitDescription = `Pass an \`onSubmit\` callback function to execute some code after a user submits the search form.
|
|
130
131
|
|
|
131
132
|
Your callback function will be invoked with a submit event containing useful metadata about the submit event:
|
|
@@ -37,7 +37,7 @@ const convertLegacyParametersAndAddDefaults = (sections) => sections.map((config
|
|
|
37
37
|
const useCioAutocomplete = (options) => {
|
|
38
38
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
39
|
const memoizedOptions = useMemo(() => options, [JSON.stringify(options)]);
|
|
40
|
-
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, cioJsClientOptions, placeholder = 'What can we help you find today?', autocompleteClassName = 'cio-autocomplete', advancedParameters, defaultInput, getSearchResultsUrl, } = memoizedOptions;
|
|
40
|
+
const { onSubmit, onChange, openOnFocus, apiKey, cioJsClient, cioJsClientOptions, placeholder = 'What can we help you find today?', autocompleteClassName = 'cio-autocomplete', advancedParameters, defaultInput, getSearchResultsUrl, onIsOpenChange, } = memoizedOptions;
|
|
41
41
|
let { sections = defaultSections, zeroStateSections } = memoizedOptions;
|
|
42
42
|
sections = useMemo(() => {
|
|
43
43
|
if (sections) {
|
|
@@ -59,7 +59,7 @@ const useCioAutocomplete = (options) => {
|
|
|
59
59
|
const features = useMemo(() => getFeatures(request), [request]);
|
|
60
60
|
// Get dropdown items array from active sections (autocomplete + recommendations + custom)
|
|
61
61
|
const items = useMemo(() => getItemsForActiveSections(activeSectionsWithData), [activeSectionsWithData]);
|
|
62
|
-
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex, getInputProps, getItemProps: getItemPropsDownShift, } = useDownShift({ setQuery, items, onSubmit, cioClient, previousQuery });
|
|
62
|
+
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex, getInputProps, getItemProps: getItemPropsDownShift, } = useDownShift({ setQuery, items, onSubmit, cioClient, previousQuery, onIsOpenChange });
|
|
63
63
|
// Log console errors
|
|
64
64
|
useConsoleErrors(sections, activeSections);
|
|
65
65
|
// Track recommendation view
|
|
@@ -117,7 +117,7 @@ const useCioAutocomplete = (options) => {
|
|
|
117
117
|
openMenu();
|
|
118
118
|
}
|
|
119
119
|
try {
|
|
120
|
-
if (advancedParameters?.fetchZeroStateOnFocus
|
|
120
|
+
if (advancedParameters?.fetchZeroStateOnFocus) {
|
|
121
121
|
fetchRecommendationResults();
|
|
122
122
|
}
|
|
123
123
|
cioClient?.tracker?.trackInputFocus();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCombobox } from 'downshift';
|
|
2
2
|
import { trackSearchSubmit } from '../utils';
|
|
3
3
|
let idCounter = 0;
|
|
4
|
-
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = '' }) => useCombobox({
|
|
4
|
+
const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = '', onIsOpenChange, }) => useCombobox({
|
|
5
5
|
id: `cio-autocomplete-${idCounter++}`,
|
|
6
6
|
items,
|
|
7
7
|
itemToString: (item) => item?.value || '',
|
|
@@ -47,5 +47,6 @@ const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = ''
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
|
+
onIsOpenChange,
|
|
50
51
|
});
|
|
51
52
|
export default useDownShift;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* eslint-disable max-params */
|
|
2
|
+
import { createRef, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import useActiveSections from './useActiveSections';
|
|
4
|
+
import useSectionsResults from './useSectionsResults';
|
|
5
|
+
import useActiveSectionsWithData from './useActiveSectionsWithData';
|
|
6
|
+
import useRemoveSections from './useRemoveSections';
|
|
7
|
+
export default function useSections(query, cioClient, sections, zeroStateSections, advancedParameters) {
|
|
8
|
+
const [podsData, setPodsData] = useState({});
|
|
9
|
+
// Get Active Sections defined by configuration object
|
|
10
|
+
const { activeSections, showZeroStateSections, setActiveSections } = useActiveSections(query, sections, podsData, zeroStateSections);
|
|
11
|
+
// create refs for active sections
|
|
12
|
+
const sectionsRefs = useRef(activeSections.map(() => createRef()));
|
|
13
|
+
// Get API results for each active section
|
|
14
|
+
const { recommendations, autocomplete } = useSectionsResults(query, cioClient, activeSections, advancedParameters);
|
|
15
|
+
// Access the Pods Data from the Recommendations response to update Active Sections configuration
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!recommendations.podsData) {
|
|
18
|
+
setPodsData(recommendations.podsData);
|
|
19
|
+
}
|
|
20
|
+
}, [recommendations.podsData]);
|
|
21
|
+
useRemoveSections(sections, podsData, setActiveSections, showZeroStateSections, zeroStateSections);
|
|
22
|
+
// Combine recommendations and autocomplete results in sectionsResults
|
|
23
|
+
const sectionsResults = useMemo(() => ({ ...autocomplete.results, ...recommendations.results }), [autocomplete.results, recommendations.results]);
|
|
24
|
+
// Return current active sections populated with data from the API response sectionsResults
|
|
25
|
+
const activeSectionsWithData = useActiveSectionsWithData(sectionsResults, activeSections, sectionsRefs, query);
|
|
26
|
+
return {
|
|
27
|
+
fetchRecommendationResults: recommendations.fetchRecommendationResults,
|
|
28
|
+
activeSections,
|
|
29
|
+
activeSectionsWithData,
|
|
30
|
+
zeroStateActiveSections: showZeroStateSections,
|
|
31
|
+
request: autocomplete.request,
|
|
32
|
+
totalNumResultsPerSection: autocomplete.totalNumResultsPerSection,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
import { isRecommendationsSection } from '../../typeGuards';
|
|
3
|
+
export default function useActiveSections(query, sections, podsData, zeroStateSections) {
|
|
4
|
+
const showZeroStateSections = !query.length && !!zeroStateSections?.length;
|
|
5
|
+
const [activeSections, setActiveSections] = useState(showZeroStateSections ? zeroStateSections : sections);
|
|
6
|
+
// Merge Recommendation Pods Display Name from Dashboard
|
|
7
|
+
const activeSectionsTransformed = useMemo(() => activeSections.map((config) => {
|
|
8
|
+
const mergedConfig = config;
|
|
9
|
+
if (isRecommendationsSection(config)) {
|
|
10
|
+
const podData = podsData?.[config.podId];
|
|
11
|
+
const libraryDisplayName = config.displayName;
|
|
12
|
+
const dashboardDisplayName = podData?.displayName;
|
|
13
|
+
mergedConfig.displayName = libraryDisplayName || dashboardDisplayName;
|
|
14
|
+
}
|
|
15
|
+
return mergedConfig;
|
|
16
|
+
}), [activeSections, podsData]);
|
|
17
|
+
return {
|
|
18
|
+
activeSections: activeSectionsTransformed,
|
|
19
|
+
showZeroStateSections,
|
|
20
|
+
setActiveSections,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { getActiveSectionsWithData } from '../../utils';
|
|
3
|
+
export default function useActiveSectionsWithData(sectionsResults, activeSections, sectionsRefs, query) {
|
|
4
|
+
const [activeSectionsWithData, setActiveSectionsWithData] = useState([]);
|
|
5
|
+
// Add to active sections the results data and refs when autocomplete results or recommendation results fetched
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const activeSectionsWithDataValue = getActiveSectionsWithData(activeSections, sectionsResults, sectionsRefs);
|
|
8
|
+
if (activeSectionsWithDataValue.length || !query) {
|
|
9
|
+
setActiveSectionsWithData(activeSectionsWithDataValue);
|
|
10
|
+
}
|
|
11
|
+
}, [activeSections, sectionsResults, sectionsRefs, query]);
|
|
12
|
+
return activeSectionsWithData;
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* eslint-disable max-params */
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { getFeatures } from '../../utils';
|
|
4
|
+
export default function useRemoveSections(sections, podsData, setActiveSections, showZeroStateSections, zeroStateSections) {
|
|
5
|
+
// Remove sections if necessary
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const features = getFeatures(Object.values(podsData || {})?.[0]?.request);
|
|
8
|
+
if (showZeroStateSections) {
|
|
9
|
+
if (!features.featureDisplayZeroStateRecommendations) {
|
|
10
|
+
setActiveSections([]);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
setActiveSections(zeroStateSections);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
setActiveSections(sections);
|
|
18
|
+
}
|
|
19
|
+
}, [zeroStateSections, showZeroStateSections, sections, podsData, setActiveSections]);
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import useDebouncedFetchSection from '../useDebouncedFetchSections';
|
|
3
|
+
import useFetchRecommendationPod from '../useFetchRecommendationPod';
|
|
4
|
+
import { isAutocompleteSection, isRecommendationsSection } from '../../typeGuards';
|
|
5
|
+
export default function useSectionsResults(query, cioClient, activeSections, advancedParameters) {
|
|
6
|
+
// Fetch Autocomplete Results
|
|
7
|
+
const activeAutocompleteSections = useMemo(() => activeSections?.filter((config) => isAutocompleteSection(config)), [activeSections]);
|
|
8
|
+
const { sectionsData: autocompleteResults, request, totalNumResultsPerSection, } = useDebouncedFetchSection(query, cioClient, activeAutocompleteSections, advancedParameters);
|
|
9
|
+
// Fetch Recommendations Results
|
|
10
|
+
const activeRecommendationsSections = useMemo(() => activeSections?.filter((config) => isRecommendationsSection(config)), [activeSections]);
|
|
11
|
+
const { fetchRecommendationResults, recommendationsResults, podsData } = useFetchRecommendationPod(cioClient, activeRecommendationsSections, advancedParameters?.fetchZeroStateOnFocus);
|
|
12
|
+
return {
|
|
13
|
+
recommendations: {
|
|
14
|
+
results: recommendationsResults,
|
|
15
|
+
fetchRecommendationResults,
|
|
16
|
+
podsData,
|
|
17
|
+
},
|
|
18
|
+
autocomplete: {
|
|
19
|
+
results: autocompleteResults,
|
|
20
|
+
request,
|
|
21
|
+
totalNumResultsPerSection,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
package/lib/mjs/utils.js
CHANGED
|
@@ -130,14 +130,14 @@ export const getCioClient = (apiKey, cioJsClientOptions) => {
|
|
|
130
130
|
}
|
|
131
131
|
return null;
|
|
132
132
|
};
|
|
133
|
-
export const getActiveSectionsWithData = (activeSections,
|
|
133
|
+
export const getActiveSectionsWithData = (activeSections, sectionsResults, sectionsRefs) => {
|
|
134
134
|
const activeSectionsWithData = [];
|
|
135
|
-
|
|
135
|
+
const getSectionData = (sectionConfig) => {
|
|
136
136
|
const { type } = sectionConfig;
|
|
137
137
|
let sectionData;
|
|
138
138
|
switch (type) {
|
|
139
139
|
case 'recommendations':
|
|
140
|
-
sectionData =
|
|
140
|
+
sectionData = sectionsResults[sectionConfig.podId];
|
|
141
141
|
break;
|
|
142
142
|
case 'custom':
|
|
143
143
|
// Copy id from data to the top level
|
|
@@ -148,8 +148,12 @@ export const getActiveSectionsWithData = (activeSections, sectionResults, sectio
|
|
|
148
148
|
break;
|
|
149
149
|
default:
|
|
150
150
|
// Autocomplete
|
|
151
|
-
sectionData =
|
|
151
|
+
sectionData = sectionsResults[sectionConfig.indexSectionName];
|
|
152
152
|
}
|
|
153
|
+
return sectionData;
|
|
154
|
+
};
|
|
155
|
+
activeSections?.forEach((sectionConfig, index) => {
|
|
156
|
+
const sectionData = getSectionData(sectionConfig);
|
|
153
157
|
if (Array.isArray(sectionData)) {
|
|
154
158
|
const section = {
|
|
155
159
|
...sectionConfig,
|
package/lib/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.23.
|
|
1
|
+
export default '1.23.19';
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export declare const displaySearchTermHighlightsDescription = "Use constructor's
|
|
|
20
20
|
export declare const customSectionDescription = "Use a custom section, by managing and passing your own data, with `\"type\": \"custom\"` and `\"data\":[{...}]`";
|
|
21
21
|
export declare const onFocusDescription = "Pass an `onFocus` callback function to execute some code each time the user applies focus to the text input field";
|
|
22
22
|
export declare const onChangeDescription = "Pass an `onChange` callback function to execute some code each time the user changes the value of the text input field";
|
|
23
|
+
export declare const onIsOpenChangeDescription = "Pass an `onIsOpenChange` callback function to execute some code each time the dropdown state changes ie: close/open";
|
|
23
24
|
export declare const onSubmitDescription = "Pass an `onSubmit` callback function to execute some code after a user submits the search form.\n\n Your callback function will be invoked with a submit event containing useful metadata about the submit event:\n - if the user submits the text input:\n - the `query` field will provide the value of that input field\n - if the user selects a suggested item from the dropdown list:\n - the `originalQuery` field will provide the value of the input field that generated the selected item\n - an `item` object with information about the suggestion that the user selected";
|
|
24
25
|
export declare const onSubmitDefault: (submitEvent: AutocompleteSubmitEvent) => void;
|
|
25
26
|
export declare const zeroStateSectionsDescription = "Use `zeroStateSections` to show suggestions after a user applies focus to the search input field and before they start typing a query";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { UseComboboxReturnValue } from 'downshift';
|
|
2
|
+
import { UseComboboxReturnValue, UseComboboxStateChange } from 'downshift';
|
|
3
3
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
4
4
|
import { Nullable } from '@constructor-io/constructorio-client-javascript/lib/types';
|
|
5
5
|
import { Item, OnSubmit } from '../types';
|
|
@@ -9,6 +9,7 @@ type UseDownShiftOptions = {
|
|
|
9
9
|
onSubmit: OnSubmit;
|
|
10
10
|
previousQuery?: string;
|
|
11
11
|
cioClient: Nullable<ConstructorIOClient>;
|
|
12
|
+
onIsOpenChange?: (changes: UseComboboxStateChange<Item>) => void;
|
|
12
13
|
};
|
|
13
14
|
export type DownShift = UseComboboxReturnValue<Item>;
|
|
14
15
|
type UseDownShift = (options: UseDownShiftOptions) => DownShift;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import ConstructorIO from '@constructor-io/constructorio-client-javascript';
|
|
2
2
|
import { Nullable } from '@constructor-io/constructorio-client-javascript/lib/types';
|
|
3
|
-
import { AdvancedParameters, UserDefinedSection
|
|
3
|
+
import { AdvancedParameters, UserDefinedSection } from '../../types';
|
|
4
4
|
export default function useSections(query: string, cioClient: Nullable<ConstructorIO>, sections: UserDefinedSection[], zeroStateSections?: UserDefinedSection[], advancedParameters?: AdvancedParameters): {
|
|
5
5
|
fetchRecommendationResults: () => Promise<void>;
|
|
6
6
|
activeSections: UserDefinedSection[];
|
|
7
|
-
activeSectionsWithData: Section[];
|
|
8
|
-
zeroStateActiveSections:
|
|
7
|
+
activeSectionsWithData: import("../../types").Section[];
|
|
8
|
+
zeroStateActiveSections: boolean;
|
|
9
9
|
request: Partial<import("@constructor-io/constructorio-client-javascript").AutocompleteRequestType>;
|
|
10
10
|
totalNumResultsPerSection: Record<string, number>;
|
|
11
11
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PodData, UserDefinedSection } from '../../types';
|
|
2
|
+
export default function useActiveSections(query: string, sections: UserDefinedSection[], podsData?: Record<string, PodData>, zeroStateSections?: UserDefinedSection[]): {
|
|
3
|
+
activeSections: UserDefinedSection[];
|
|
4
|
+
showZeroStateSections: boolean;
|
|
5
|
+
setActiveSections: import("react").Dispatch<import("react").SetStateAction<UserDefinedSection[]>>;
|
|
6
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { UserDefinedSection, Section, SectionsData } from '../../types';
|
|
3
|
+
export default function useActiveSectionsWithData(sectionsResults: SectionsData, activeSections: UserDefinedSection[], sectionsRefs: React.MutableRefObject<RefObject<HTMLLIElement>[]>, query: string): Section[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ConstructorIO from '@constructor-io/constructorio-client-javascript';
|
|
2
|
+
import { Nullable } from '@constructor-io/constructorio-client-javascript/lib/types';
|
|
3
|
+
import { AdvancedParameters, UserDefinedSection } from '../../types';
|
|
4
|
+
export default function useSectionsResults(query: string, cioClient: Nullable<ConstructorIO>, activeSections: UserDefinedSection[], advancedParameters?: AdvancedParameters): {
|
|
5
|
+
recommendations: {
|
|
6
|
+
results: import("../../types").SectionsData;
|
|
7
|
+
fetchRecommendationResults: () => Promise<void>;
|
|
8
|
+
podsData: Record<string, import("../../types").PodData>;
|
|
9
|
+
};
|
|
10
|
+
autocomplete: {
|
|
11
|
+
results: import("../../types").SectionsData;
|
|
12
|
+
request: Partial<import("@constructor-io/constructorio-client-javascript").AutocompleteRequestType>;
|
|
13
|
+
totalNumResultsPerSection: Record<string, number>;
|
|
14
|
+
};
|
|
15
|
+
};
|
package/lib/types/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GetItemPropsOptions } from 'downshift';
|
|
1
|
+
import { GetItemPropsOptions, UseComboboxStateChange } from 'downshift';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
4
4
|
import { IAutocompleteParameters, SearchSuggestion as SearchSuggestionFromClient, Product as ProductFromClient, Item as ItemBase, AutocompleteRequestType, ConstructorClientOptions, RecommendationsParameters } from '@constructor-io/constructorio-client-javascript/lib/types';
|
|
@@ -25,6 +25,7 @@ export type CioAutocompleteProps = CioClientConfig & {
|
|
|
25
25
|
onSubmit: OnSubmit;
|
|
26
26
|
onFocus?: () => void;
|
|
27
27
|
onChange?: (input: string) => void;
|
|
28
|
+
onIsOpenChange?: (changes: UseComboboxStateChange<Item>) => void;
|
|
28
29
|
placeholder?: string;
|
|
29
30
|
children?: ReactNode;
|
|
30
31
|
sections?: UserDefinedSection[];
|
package/lib/types/utils.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare const functionStrings: {
|
|
|
37
37
|
export declare const stringifyWithDefaults: (obj: any) => string;
|
|
38
38
|
export declare const disableStoryActions: (story: any) => void;
|
|
39
39
|
export declare const getCioClient: (apiKey?: string, cioJsClientOptions?: ConstructorClientOptions) => ConstructorIOClient | null;
|
|
40
|
-
export declare const getActiveSectionsWithData: (activeSections: UserDefinedSection[],
|
|
40
|
+
export declare const getActiveSectionsWithData: (activeSections: UserDefinedSection[], sectionsResults: SectionsData, sectionsRefs: React.MutableRefObject<React.RefObject<HTMLLIElement>[]>) => Section[];
|
|
41
41
|
export declare const escapeRegExp: (string: string) => string;
|
|
42
42
|
export declare const trackRecommendationView: (target: HTMLElement, activeSectionsWithData: Section[], cioClient: Nullable<ConstructorIOClient>) => void;
|
|
43
43
|
export declare const getItemsForActiveSections: (activeSectionsWithData: Section[]) => Item[];
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "1.23.
|
|
1
|
+
declare const _default: "1.23.19";
|
|
2
2
|
export default _default;
|
package/package.json
CHANGED