@constructor-io/constructorio-ui-autocomplete 1.20.1 → 1.20.3
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 +1 -1
- package/dist/constructorio-ui-autocomplete-bundled.js +13 -13
- package/lib/cjs/components/Autocomplete/SectionItemsList/SectionItemsList.js +2 -1
- package/lib/cjs/constants.js +1 -1
- package/lib/cjs/hooks/useCioAutocomplete.js +42 -24
- package/lib/cjs/hooks/useFetchRecommendationPod.js +6 -2
- package/lib/cjs/hooks/useSections.js +18 -2
- package/lib/cjs/utils.js +8 -3
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/Autocomplete/SectionItemsList/SectionItemsList.js +2 -1
- package/lib/mjs/constants.js +1 -1
- package/lib/mjs/hooks/useCioAutocomplete.js +40 -23
- package/lib/mjs/hooks/useFetchRecommendationPod.js +6 -2
- package/lib/mjs/hooks/useSections.js +18 -3
- package/lib/mjs/utils.js +6 -1
- package/lib/mjs/version.js +1 -1
- package/lib/styles.css +13 -5
- package/lib/types/components/Autocomplete/CioAutocompleteProvider.d.ts +1 -0
- package/lib/types/constants.d.ts +1 -1
- package/lib/types/hooks/useCioAutocomplete.d.ts +1 -0
- package/lib/types/hooks/useSections.d.ts +1 -1
- package/lib/types/types.d.ts +1 -0
- package/lib/types/utils.d.ts +2 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -29,8 +29,9 @@ const DefaultRenderSectionItemsList = function ({ section }) {
|
|
|
29
29
|
}
|
|
30
30
|
if (!((_a = section === null || section === void 0 ? void 0 : section.data) === null || _a === void 0 ? void 0 : _a.length))
|
|
31
31
|
return null;
|
|
32
|
+
// @deprecated `cio-sectionName` will be removed in the next major release
|
|
32
33
|
return (react_1.default.createElement("li", Object.assign({}, getSectionProps(section)),
|
|
33
|
-
react_1.default.createElement("h5", { className: 'cio-sectionName', "aria-hidden": true }, (0, utils_1.camelToStartCase)(sectionTitle)),
|
|
34
|
+
react_1.default.createElement("h5", { className: 'cio-section-name cio-sectionName', "aria-hidden": true }, (0, utils_1.camelToStartCase)(sectionTitle)),
|
|
34
35
|
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, displaySearchTermHighlights: section.displaySearchTermHighlights }))))));
|
|
35
36
|
};
|
|
36
37
|
function SectionItemsList(props) {
|
package/lib/cjs/constants.js
CHANGED
|
@@ -182,7 +182,7 @@ import '@constructor-io/constructorio-ui-autocomplete/styles.css';
|
|
|
182
182
|
right: 24px;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
.cio-autocomplete.custom-autocomplete-styles .cio-
|
|
185
|
+
.cio-autocomplete.custom-autocomplete-styles .cio-section-name {
|
|
186
186
|
margin: 5px 3px;
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -58,6 +58,7 @@ const useCioAutocomplete = (options) => {
|
|
|
58
58
|
const cioClient = (0, useCioClient_1.default)({ apiKey, cioJsClient, cioJsClientOptions });
|
|
59
59
|
// Get autocomplete sections (autocomplete + recommendations + custom)
|
|
60
60
|
const { activeSections, activeSectionsWithData, zeroStateActiveSections, request } = (0, useSections_1.default)(query, cioClient, sections, zeroStateSections, advancedParameters);
|
|
61
|
+
const features = (0, react_1.useMemo)(() => (0, utils_1.getFeatures)(request), [request]);
|
|
61
62
|
// Get dropdown items array from active sections (autocomplete + recommendations + custom)
|
|
62
63
|
const items = (0, react_1.useMemo)(() => (0, utils_1.getItemsForActiveSections)(activeSectionsWithData), [activeSectionsWithData]);
|
|
63
64
|
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex, getInputProps, getItemProps, } = (0, useDownShift_1.default)({ setQuery, items, onSubmit, cioClient, previousQuery });
|
|
@@ -69,7 +70,7 @@ const useCioAutocomplete = (options) => {
|
|
|
69
70
|
query,
|
|
70
71
|
sections: activeSectionsWithData,
|
|
71
72
|
request,
|
|
72
|
-
featureToggles:
|
|
73
|
+
featureToggles: features,
|
|
73
74
|
isOpen: isOpen && (items === null || items === void 0 ? void 0 : items.length) > 0,
|
|
74
75
|
getMenuProps: () => (Object.assign(Object.assign({}, getMenuProps()), { className: 'cio-results', 'data-testid': 'cio-results' })),
|
|
75
76
|
getLabelProps,
|
|
@@ -78,7 +79,9 @@ const useCioAutocomplete = (options) => {
|
|
|
78
79
|
getItemProps: (item) => {
|
|
79
80
|
const { index, sectionId } = (0, utils_1.getItemPosition)({ item, items });
|
|
80
81
|
const sectionItemTestId = `cio-item-${sectionId === null || sectionId === void 0 ? void 0 : sectionId.replace(' ', '')}`;
|
|
81
|
-
return Object.assign(Object.assign({}, getItemProps({ item, index })), {
|
|
82
|
+
return Object.assign(Object.assign({}, getItemProps({ item, index })), {
|
|
83
|
+
// @deprecated `sectionItemTestId` will be removed as a className in the next major version
|
|
84
|
+
className: `cio-item ${sectionItemTestId}`, 'data-testid': sectionItemTestId });
|
|
82
85
|
},
|
|
83
86
|
getInputProps: () => (Object.assign(Object.assign({}, getInputProps({
|
|
84
87
|
onChange: (e) => {
|
|
@@ -92,7 +95,9 @@ const useCioAutocomplete = (options) => {
|
|
|
92
95
|
if (options.onFocus) {
|
|
93
96
|
options.onFocus();
|
|
94
97
|
}
|
|
95
|
-
if (zeroStateActiveSections &&
|
|
98
|
+
if (zeroStateActiveSections &&
|
|
99
|
+
openOnFocus !== false &&
|
|
100
|
+
features.featureDisplayZeroStateRecommendations) {
|
|
96
101
|
openMenu();
|
|
97
102
|
}
|
|
98
103
|
if (query === null || query === void 0 ? void 0 : query.length) {
|
|
@@ -140,33 +145,46 @@ const useCioAutocomplete = (options) => {
|
|
|
140
145
|
'data-testid': 'cio-form',
|
|
141
146
|
}),
|
|
142
147
|
getSectionProps: (section) => {
|
|
143
|
-
var _a;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
var _a, _b;
|
|
149
|
+
// @deprecated ClassNames derived from this fn will be removed in the next major version
|
|
150
|
+
const getDeprecatedClassNames = () => {
|
|
151
|
+
const { type } = section;
|
|
152
|
+
let sectionTitle;
|
|
153
|
+
const indexSectionName = type !== 'custom' && section.indexSectionName
|
|
154
|
+
? (0, utils_1.toKebabCase)(section.indexSectionName)
|
|
155
|
+
: '';
|
|
156
|
+
switch (type) {
|
|
157
|
+
case 'recommendations':
|
|
158
|
+
sectionTitle = section.podId;
|
|
159
|
+
break;
|
|
160
|
+
case 'autocomplete':
|
|
161
|
+
sectionTitle = section.displayName || section.indexSectionName;
|
|
162
|
+
break;
|
|
163
|
+
case 'custom':
|
|
164
|
+
sectionTitle = section.displayName;
|
|
165
|
+
break;
|
|
166
|
+
default:
|
|
167
|
+
sectionTitle = section.displayName || section.indexSectionName;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
return `${sectionTitle} ${indexSectionName}`;
|
|
171
|
+
};
|
|
172
|
+
// Always add the indexSectionName (defaults to Products) as a class to the section container for the styles
|
|
147
173
|
// Even if the section is a recommendation pod, if the results are "Products" or "Search Suggestions"
|
|
148
174
|
// ... they should be styled accordingly
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
sectionTitle = section.podId;
|
|
153
|
-
break;
|
|
154
|
-
case 'autocomplete':
|
|
155
|
-
sectionTitle = section.displayName || section.indexSectionName;
|
|
156
|
-
break;
|
|
157
|
-
case 'custom':
|
|
158
|
-
sectionTitle = section.displayName;
|
|
159
|
-
break;
|
|
160
|
-
default:
|
|
161
|
-
sectionTitle = section.displayName || section.indexSectionName;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
175
|
+
const sectionListingType = (0, typeGuards_1.isCustomSection)(section)
|
|
176
|
+
? 'custom'
|
|
177
|
+
: (0, utils_1.toKebabCase)(section.indexSectionName || ((_a = section.data[0]) === null || _a === void 0 ? void 0 : _a.section) || 'Products');
|
|
164
178
|
const attributes = {
|
|
165
|
-
className:
|
|
179
|
+
className: `cio-section cio-section-${sectionListingType} ${getDeprecatedClassNames()}`,
|
|
166
180
|
ref: section.ref,
|
|
167
181
|
role: 'none',
|
|
168
|
-
'data-cnstrc-section': (
|
|
182
|
+
'data-cnstrc-section': (_b = section.data[0]) === null || _b === void 0 ? void 0 : _b.section,
|
|
169
183
|
};
|
|
184
|
+
if ((0, typeGuards_1.isCustomSection)(section)) {
|
|
185
|
+
attributes['data-cnstrc-custom-section'] = true;
|
|
186
|
+
attributes['data-cnstrc-custom-section-name'] = section.displayName;
|
|
187
|
+
}
|
|
170
188
|
// Add data attributes for recommendations
|
|
171
189
|
if ((0, typeGuards_1.isRecommendationsSection)(section)) {
|
|
172
190
|
attributes['data-cnstrc-recommendations'] = true;
|
|
@@ -15,14 +15,18 @@ const useFetchRecommendationPod = (cioClient, recommendationPods) => {
|
|
|
15
15
|
}));
|
|
16
16
|
const recommendationsPodResults = {};
|
|
17
17
|
const recommendationsPodsData = {};
|
|
18
|
-
responses.forEach(({ response }, index) => {
|
|
18
|
+
responses.forEach(({ response, request }, index) => {
|
|
19
19
|
const { pod, results } = response;
|
|
20
20
|
if (pod === null || pod === void 0 ? void 0 : pod.id) {
|
|
21
21
|
recommendationsPodResults[pod.id] = results === null || results === void 0 ? void 0 : results.map((item) => {
|
|
22
22
|
var _a, _b;
|
|
23
23
|
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: (_b = recommendationPods[index]) === null || _b === void 0 ? void 0 : _b.indexSectionName, podId: pod.id }));
|
|
24
24
|
});
|
|
25
|
-
recommendationsPodsData[pod.id] = {
|
|
25
|
+
recommendationsPodsData[pod.id] = {
|
|
26
|
+
displayName: pod.display_name,
|
|
27
|
+
podId: pod.id,
|
|
28
|
+
request,
|
|
29
|
+
};
|
|
26
30
|
}
|
|
27
31
|
});
|
|
28
32
|
try {
|
|
@@ -8,9 +8,9 @@ const useDebouncedFetchSections_1 = tslib_1.__importDefault(require("./useDeboun
|
|
|
8
8
|
const useFetchRecommendationPod_1 = tslib_1.__importDefault(require("./useFetchRecommendationPod"));
|
|
9
9
|
const typeGuards_1 = require("../typeGuards");
|
|
10
10
|
function useSections(query, cioClient, sections, zeroStateSections, advancedParameters) {
|
|
11
|
-
const zeroStateActiveSections = !query.length && zeroStateSections;
|
|
11
|
+
const zeroStateActiveSections = !query.length && (zeroStateSections === null || zeroStateSections === void 0 ? void 0 : zeroStateSections.length);
|
|
12
12
|
// Define All Sections
|
|
13
|
-
const activeSections = zeroStateActiveSections ? zeroStateSections : sections;
|
|
13
|
+
const [activeSections, setActiveSections] = (0, react_1.useState)(zeroStateActiveSections ? zeroStateSections : sections);
|
|
14
14
|
const sectionsRefs = (0, react_1.useRef)(activeSections.map(() => (0, react_1.createRef)()));
|
|
15
15
|
const [activeSectionsWithData, setActiveSectionsWithData] = (0, react_1.useState)([]);
|
|
16
16
|
const autocompleteSections = (0, react_1.useMemo)(() => activeSections === null || activeSections === void 0 ? void 0 : activeSections.filter((config) => (0, typeGuards_1.isAutocompleteSection)(config)), [activeSections]);
|
|
@@ -19,6 +19,22 @@ function useSections(query, cioClient, sections, zeroStateSections, advancedPara
|
|
|
19
19
|
const { sectionsData: autocompleteResults, request } = (0, useDebouncedFetchSections_1.default)(query, cioClient, autocompleteSections, advancedParameters);
|
|
20
20
|
// Fetch Recommendations Results
|
|
21
21
|
const { recommendationsResults, podsData } = (0, useFetchRecommendationPod_1.default)(cioClient, recommendationsSections);
|
|
22
|
+
// Remove sections if necessary
|
|
23
|
+
(0, react_1.useEffect)(() => {
|
|
24
|
+
var _a, _b;
|
|
25
|
+
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);
|
|
26
|
+
if (zeroStateActiveSections) {
|
|
27
|
+
if (!features.featureDisplayZeroStateRecommendations) {
|
|
28
|
+
setActiveSections([]);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
setActiveSections(zeroStateSections);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
setActiveSections(sections);
|
|
36
|
+
}
|
|
37
|
+
}, [zeroStateSections, zeroStateActiveSections, sections, podsData]);
|
|
22
38
|
// Merge Recommendation Pods Display Name from Dashboard
|
|
23
39
|
const activeSectionConfigs = (0, react_1.useMemo)(() => activeSections.map((config) => {
|
|
24
40
|
const mergedConfig = config;
|
package/lib/cjs/utils.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.translate = exports.getItemsForActiveSections = exports.trackRecommendationView = exports.escapeRegExp = exports.getActiveSectionsWithData = exports.getCioClient = exports.disableStoryActions = exports.stringifyWithDefaults = exports.functionStrings = exports.getStoryParams = exports.sleep = exports.clearConstructorRequests = exports.isTrackingRequestSent = exports.toKebabCase = exports.camelToStartCase = exports.getItemPosition = exports.
|
|
3
|
+
exports.translate = exports.getItemsForActiveSections = exports.trackRecommendationView = exports.escapeRegExp = exports.getActiveSectionsWithData = exports.getCioClient = exports.disableStoryActions = exports.stringifyWithDefaults = exports.functionStrings = exports.getStoryParams = exports.sleep = exports.clearConstructorRequests = exports.isTrackingRequestSent = exports.toKebabCase = exports.camelToStartCase = exports.getItemPosition = exports.getFeatures = 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
6
|
const typeGuards_1 = require("./typeGuards");
|
|
7
7
|
const version_1 = tslib_1.__importDefault(require("./version"));
|
|
8
|
-
function
|
|
8
|
+
function getFeatures(request) {
|
|
9
9
|
var _a, _b;
|
|
10
10
|
let featureDisplaySearchSuggestionImages = false;
|
|
11
11
|
let featureDisplaySearchSuggestionResultCounts = false;
|
|
12
|
+
let featureDisplayZeroStateRecommendations = true;
|
|
12
13
|
if (((_a = request === null || request === void 0 ? void 0 : request.features) === null || _a === void 0 ? void 0 : _a.custom_autosuggest_ui) === true) {
|
|
13
14
|
switch ((_b = request === null || request === void 0 ? void 0 : request.feature_variants) === null || _b === void 0 ? void 0 : _b.custom_autosuggest_ui) {
|
|
14
15
|
case 'custom_autosuggest_ui_result_count':
|
|
@@ -21,6 +22,9 @@ function getSearchSuggestionFeatures(request) {
|
|
|
21
22
|
featureDisplaySearchSuggestionImages = true;
|
|
22
23
|
featureDisplaySearchSuggestionResultCounts = true;
|
|
23
24
|
break;
|
|
25
|
+
case 'custom_autosuggest_ui_disable_recommendations_in_zero_state':
|
|
26
|
+
featureDisplayZeroStateRecommendations = false;
|
|
27
|
+
break;
|
|
24
28
|
default:
|
|
25
29
|
break;
|
|
26
30
|
}
|
|
@@ -28,9 +32,10 @@ function getSearchSuggestionFeatures(request) {
|
|
|
28
32
|
return {
|
|
29
33
|
featureDisplaySearchSuggestionImages,
|
|
30
34
|
featureDisplaySearchSuggestionResultCounts,
|
|
35
|
+
featureDisplayZeroStateRecommendations,
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
|
-
exports.
|
|
38
|
+
exports.getFeatures = getFeatures;
|
|
34
39
|
const getItemPosition = ({ item, items }) => {
|
|
35
40
|
var _a;
|
|
36
41
|
const index = items.findIndex((itemInFlatList) => (itemInFlatList === null || itemInFlatList === void 0 ? void 0 : itemInFlatList.id) === (item === null || item === void 0 ? void 0 : item.id));
|
package/lib/cjs/version.js
CHANGED
|
@@ -25,8 +25,9 @@ const DefaultRenderSectionItemsList = function ({ section }) {
|
|
|
25
25
|
}
|
|
26
26
|
if (!section?.data?.length)
|
|
27
27
|
return null;
|
|
28
|
+
// @deprecated `cio-sectionName` will be removed in the next major release
|
|
28
29
|
return (React.createElement("li", { ...getSectionProps(section) },
|
|
29
|
-
React.createElement("h5", { className: 'cio-sectionName', "aria-hidden": true }, camelToStartCase(sectionTitle)),
|
|
30
|
+
React.createElement("h5", { className: 'cio-section-name cio-sectionName', "aria-hidden": true }, camelToStartCase(sectionTitle)),
|
|
30
31
|
React.createElement("ul", { className: 'cio-section-items', role: 'none' }, section?.data?.map((item) => (React.createElement(SectionItem, { item: item, key: item?.id, displaySearchTermHighlights: section.displaySearchTermHighlights }))))));
|
|
31
32
|
};
|
|
32
33
|
export default function SectionItemsList(props) {
|
package/lib/mjs/constants.js
CHANGED
|
@@ -178,7 +178,7 @@ import '@constructor-io/constructorio-ui-autocomplete/styles.css';
|
|
|
178
178
|
right: 24px;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
.cio-autocomplete.custom-autocomplete-styles .cio-
|
|
181
|
+
.cio-autocomplete.custom-autocomplete-styles .cio-section-name {
|
|
182
182
|
margin: 5px 3px;
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -3,11 +3,11 @@ import { useMemo, useState } from 'react';
|
|
|
3
3
|
import useCioClient from './useCioClient';
|
|
4
4
|
import useDownShift from './useDownShift';
|
|
5
5
|
import usePrevious from './usePrevious';
|
|
6
|
-
import { getItemPosition, getItemsForActiveSections,
|
|
6
|
+
import { getItemPosition, getItemsForActiveSections, getFeatures, trackRecommendationView, toKebabCase, } from '../utils';
|
|
7
7
|
import useConsoleErrors from './useConsoleErrors';
|
|
8
8
|
import useSections from './useSections';
|
|
9
9
|
import useRecommendationsObserver from './useRecommendationsObserver';
|
|
10
|
-
import { isAutocompleteSection, isRecommendationsSection } from '../typeGuards';
|
|
10
|
+
import { isAutocompleteSection, isCustomSection, isRecommendationsSection } from '../typeGuards';
|
|
11
11
|
export const defaultSections = [
|
|
12
12
|
{
|
|
13
13
|
indexSectionName: 'Search Suggestions',
|
|
@@ -54,6 +54,7 @@ const useCioAutocomplete = (options) => {
|
|
|
54
54
|
const cioClient = useCioClient({ apiKey, cioJsClient, cioJsClientOptions });
|
|
55
55
|
// Get autocomplete sections (autocomplete + recommendations + custom)
|
|
56
56
|
const { activeSections, activeSectionsWithData, zeroStateActiveSections, request } = useSections(query, cioClient, sections, zeroStateSections, advancedParameters);
|
|
57
|
+
const features = useMemo(() => getFeatures(request), [request]);
|
|
57
58
|
// Get dropdown items array from active sections (autocomplete + recommendations + custom)
|
|
58
59
|
const items = useMemo(() => getItemsForActiveSections(activeSectionsWithData), [activeSectionsWithData]);
|
|
59
60
|
const { isOpen, getMenuProps, getLabelProps, openMenu, closeMenu, highlightedIndex, getInputProps, getItemProps, } = useDownShift({ setQuery, items, onSubmit, cioClient, previousQuery });
|
|
@@ -65,7 +66,7 @@ const useCioAutocomplete = (options) => {
|
|
|
65
66
|
query,
|
|
66
67
|
sections: activeSectionsWithData,
|
|
67
68
|
request,
|
|
68
|
-
featureToggles:
|
|
69
|
+
featureToggles: features,
|
|
69
70
|
isOpen: isOpen && items?.length > 0,
|
|
70
71
|
getMenuProps: () => ({
|
|
71
72
|
...getMenuProps(),
|
|
@@ -80,6 +81,7 @@ const useCioAutocomplete = (options) => {
|
|
|
80
81
|
const sectionItemTestId = `cio-item-${sectionId?.replace(' ', '')}`;
|
|
81
82
|
return {
|
|
82
83
|
...getItemProps({ item, index }),
|
|
84
|
+
// @deprecated `sectionItemTestId` will be removed as a className in the next major version
|
|
83
85
|
className: `cio-item ${sectionItemTestId}`,
|
|
84
86
|
'data-testid': sectionItemTestId,
|
|
85
87
|
};
|
|
@@ -98,7 +100,9 @@ const useCioAutocomplete = (options) => {
|
|
|
98
100
|
if (options.onFocus) {
|
|
99
101
|
options.onFocus();
|
|
100
102
|
}
|
|
101
|
-
if (zeroStateActiveSections &&
|
|
103
|
+
if (zeroStateActiveSections &&
|
|
104
|
+
openOnFocus !== false &&
|
|
105
|
+
features.featureDisplayZeroStateRecommendations) {
|
|
102
106
|
openMenu();
|
|
103
107
|
}
|
|
104
108
|
if (query?.length) {
|
|
@@ -151,32 +155,45 @@ const useCioAutocomplete = (options) => {
|
|
|
151
155
|
'data-testid': 'cio-form',
|
|
152
156
|
}),
|
|
153
157
|
getSectionProps: (section) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
// @deprecated ClassNames derived from this fn will be removed in the next major version
|
|
159
|
+
const getDeprecatedClassNames = () => {
|
|
160
|
+
const { type } = section;
|
|
161
|
+
let sectionTitle;
|
|
162
|
+
const indexSectionName = type !== 'custom' && section.indexSectionName
|
|
163
|
+
? toKebabCase(section.indexSectionName)
|
|
164
|
+
: '';
|
|
165
|
+
switch (type) {
|
|
166
|
+
case 'recommendations':
|
|
167
|
+
sectionTitle = section.podId;
|
|
168
|
+
break;
|
|
169
|
+
case 'autocomplete':
|
|
170
|
+
sectionTitle = section.displayName || section.indexSectionName;
|
|
171
|
+
break;
|
|
172
|
+
case 'custom':
|
|
173
|
+
sectionTitle = section.displayName;
|
|
174
|
+
break;
|
|
175
|
+
default:
|
|
176
|
+
sectionTitle = section.displayName || section.indexSectionName;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
return `${sectionTitle} ${indexSectionName}`;
|
|
180
|
+
};
|
|
181
|
+
// Always add the indexSectionName (defaults to Products) as a class to the section container for the styles
|
|
157
182
|
// Even if the section is a recommendation pod, if the results are "Products" or "Search Suggestions"
|
|
158
183
|
// ... they should be styled accordingly
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
sectionTitle = section.podId;
|
|
163
|
-
break;
|
|
164
|
-
case 'autocomplete':
|
|
165
|
-
sectionTitle = section.displayName || section.indexSectionName;
|
|
166
|
-
break;
|
|
167
|
-
case 'custom':
|
|
168
|
-
sectionTitle = section.displayName;
|
|
169
|
-
break;
|
|
170
|
-
default:
|
|
171
|
-
sectionTitle = section.displayName || section.indexSectionName;
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
184
|
+
const sectionListingType = isCustomSection(section)
|
|
185
|
+
? 'custom'
|
|
186
|
+
: toKebabCase(section.indexSectionName || section.data[0]?.section || 'Products');
|
|
174
187
|
const attributes = {
|
|
175
|
-
className:
|
|
188
|
+
className: `cio-section cio-section-${sectionListingType} ${getDeprecatedClassNames()}`,
|
|
176
189
|
ref: section.ref,
|
|
177
190
|
role: 'none',
|
|
178
191
|
'data-cnstrc-section': section.data[0]?.section,
|
|
179
192
|
};
|
|
193
|
+
if (isCustomSection(section)) {
|
|
194
|
+
attributes['data-cnstrc-custom-section'] = true;
|
|
195
|
+
attributes['data-cnstrc-custom-section-name'] = section.displayName;
|
|
196
|
+
}
|
|
180
197
|
// Add data attributes for recommendations
|
|
181
198
|
if (isRecommendationsSection(section)) {
|
|
182
199
|
attributes['data-cnstrc-recommendations'] = true;
|
|
@@ -12,7 +12,7 @@ const useFetchRecommendationPod = (cioClient, recommendationPods) => {
|
|
|
12
12
|
})));
|
|
13
13
|
const recommendationsPodResults = {};
|
|
14
14
|
const recommendationsPodsData = {};
|
|
15
|
-
responses.forEach(({ response }, index) => {
|
|
15
|
+
responses.forEach(({ response, request }, index) => {
|
|
16
16
|
const { pod, results } = response;
|
|
17
17
|
if (pod?.id) {
|
|
18
18
|
recommendationsPodResults[pod.id] = results?.map((item) => ({
|
|
@@ -21,7 +21,11 @@ const useFetchRecommendationPod = (cioClient, recommendationPods) => {
|
|
|
21
21
|
section: recommendationPods[index]?.indexSectionName,
|
|
22
22
|
podId: pod.id,
|
|
23
23
|
}));
|
|
24
|
-
recommendationsPodsData[pod.id] = {
|
|
24
|
+
recommendationsPodsData[pod.id] = {
|
|
25
|
+
displayName: pod.display_name,
|
|
26
|
+
podId: pod.id,
|
|
27
|
+
request,
|
|
28
|
+
};
|
|
25
29
|
}
|
|
26
30
|
});
|
|
27
31
|
try {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable max-params */
|
|
2
2
|
import { createRef, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { getActiveSectionsWithData } from '../utils';
|
|
3
|
+
import { getActiveSectionsWithData, getFeatures } from '../utils';
|
|
4
4
|
import useDebouncedFetchSection from './useDebouncedFetchSections';
|
|
5
5
|
import useFetchRecommendationPod from './useFetchRecommendationPod';
|
|
6
6
|
import { isAutocompleteSection, isRecommendationsSection } from '../typeGuards';
|
|
7
7
|
export default function useSections(query, cioClient, sections, zeroStateSections, advancedParameters) {
|
|
8
|
-
const zeroStateActiveSections = !query.length && zeroStateSections;
|
|
8
|
+
const zeroStateActiveSections = !query.length && zeroStateSections?.length;
|
|
9
9
|
// Define All Sections
|
|
10
|
-
const activeSections = zeroStateActiveSections ? zeroStateSections : sections;
|
|
10
|
+
const [activeSections, setActiveSections] = useState(zeroStateActiveSections ? zeroStateSections : sections);
|
|
11
11
|
const sectionsRefs = useRef(activeSections.map(() => createRef()));
|
|
12
12
|
const [activeSectionsWithData, setActiveSectionsWithData] = useState([]);
|
|
13
13
|
const autocompleteSections = useMemo(() => activeSections?.filter((config) => isAutocompleteSection(config)), [activeSections]);
|
|
@@ -16,6 +16,21 @@ export default function useSections(query, cioClient, sections, zeroStateSection
|
|
|
16
16
|
const { sectionsData: autocompleteResults, request } = useDebouncedFetchSection(query, cioClient, autocompleteSections, advancedParameters);
|
|
17
17
|
// Fetch Recommendations Results
|
|
18
18
|
const { recommendationsResults, podsData } = useFetchRecommendationPod(cioClient, recommendationsSections);
|
|
19
|
+
// Remove sections if necessary
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const features = getFeatures(Object.values(podsData || {})?.[0]?.request);
|
|
22
|
+
if (zeroStateActiveSections) {
|
|
23
|
+
if (!features.featureDisplayZeroStateRecommendations) {
|
|
24
|
+
setActiveSections([]);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
setActiveSections(zeroStateSections);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
setActiveSections(sections);
|
|
32
|
+
}
|
|
33
|
+
}, [zeroStateSections, zeroStateActiveSections, sections, podsData]);
|
|
19
34
|
// Merge Recommendation Pods Display Name from Dashboard
|
|
20
35
|
const activeSectionConfigs = useMemo(() => activeSections.map((config) => {
|
|
21
36
|
const mergedConfig = config;
|
package/lib/mjs/utils.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
2
|
import { isRecommendationsSection } from './typeGuards';
|
|
3
3
|
import version from './version';
|
|
4
|
-
export function
|
|
4
|
+
export function getFeatures(request) {
|
|
5
5
|
let featureDisplaySearchSuggestionImages = false;
|
|
6
6
|
let featureDisplaySearchSuggestionResultCounts = false;
|
|
7
|
+
let featureDisplayZeroStateRecommendations = true;
|
|
7
8
|
if (request?.features?.custom_autosuggest_ui === true) {
|
|
8
9
|
switch (request?.feature_variants?.custom_autosuggest_ui) {
|
|
9
10
|
case 'custom_autosuggest_ui_result_count':
|
|
@@ -16,6 +17,9 @@ export function getSearchSuggestionFeatures(request) {
|
|
|
16
17
|
featureDisplaySearchSuggestionImages = true;
|
|
17
18
|
featureDisplaySearchSuggestionResultCounts = true;
|
|
18
19
|
break;
|
|
20
|
+
case 'custom_autosuggest_ui_disable_recommendations_in_zero_state':
|
|
21
|
+
featureDisplayZeroStateRecommendations = false;
|
|
22
|
+
break;
|
|
19
23
|
default:
|
|
20
24
|
break;
|
|
21
25
|
}
|
|
@@ -23,6 +27,7 @@ export function getSearchSuggestionFeatures(request) {
|
|
|
23
27
|
return {
|
|
24
28
|
featureDisplaySearchSuggestionImages,
|
|
25
29
|
featureDisplaySearchSuggestionResultCounts,
|
|
30
|
+
featureDisplayZeroStateRecommendations,
|
|
26
31
|
};
|
|
27
32
|
}
|
|
28
33
|
export const getItemPosition = ({ item, items }) => {
|
package/lib/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.20.
|
|
1
|
+
export default '1.20.3';
|
package/lib/styles.css
CHANGED
|
@@ -61,7 +61,9 @@
|
|
|
61
61
|
margin-top: 5px;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
/* @deprecated in favor of .cio-section-name */
|
|
65
|
+
.cio-autocomplete .cio-sectionName,
|
|
66
|
+
.cio-autocomplete .cio-section-name {
|
|
65
67
|
margin: 15px 0;
|
|
66
68
|
font-size: 1rem;
|
|
67
69
|
}
|
|
@@ -70,7 +72,9 @@
|
|
|
70
72
|
padding: 0;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
/* @deprecated in favor of .cio-section-search-suggestions */
|
|
76
|
+
.cio-autocomplete .cio-item.cio-item-SearchSuggestions,
|
|
77
|
+
.cio-autocomplete .cio-section-search-suggestions .cio-item {
|
|
74
78
|
flex-direction: row;
|
|
75
79
|
min-width: 160px;
|
|
76
80
|
justify-content: space-between;
|
|
@@ -92,11 +96,15 @@
|
|
|
92
96
|
border-radius: 4px;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
.cio-
|
|
99
|
+
/* @deprecated in favor of .cio-section-products */
|
|
100
|
+
.cio-autocomplete .products,
|
|
101
|
+
.cio-autocomplete .cio-section-products {
|
|
96
102
|
padding: 0 5px;
|
|
97
103
|
}
|
|
98
104
|
|
|
99
|
-
.cio-
|
|
105
|
+
/* @deprecated in favor of .cio-section-products .cio-item */
|
|
106
|
+
.cio-autocomplete .products .cio-item,
|
|
107
|
+
.cio-autocomplete .cio-section-products .cio-item {
|
|
100
108
|
display: inline-flex;
|
|
101
109
|
align-items: center;
|
|
102
110
|
width: 25%;
|
|
@@ -134,4 +142,4 @@
|
|
|
134
142
|
.cio-autocomplete .cio-suggestion-count {
|
|
135
143
|
font-size: 0.8rem;
|
|
136
144
|
margin-left: 8px;
|
|
137
|
-
}
|
|
145
|
+
}
|
|
@@ -7,6 +7,7 @@ export declare const CioAutocompleteContext: React.Context<{
|
|
|
7
7
|
featureToggles: {
|
|
8
8
|
featureDisplaySearchSuggestionImages: boolean;
|
|
9
9
|
featureDisplaySearchSuggestionResultCounts: boolean;
|
|
10
|
+
featureDisplayZeroStateRecommendations: boolean;
|
|
10
11
|
};
|
|
11
12
|
isOpen: boolean;
|
|
12
13
|
getMenuProps: () => any;
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare const onSubmitDefault: (submitEvent: AutocompleteSubmitEvent) =>
|
|
|
25
25
|
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";
|
|
26
26
|
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";
|
|
27
27
|
export declare const multipleSectionsDescription = "Use as many different `recommendations` and `custom` sections as you'd like and in whatever order you would like!";
|
|
28
|
-
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-
|
|
28
|
+
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-section-name {\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";
|
|
29
29
|
export declare const advancedParametersDescription = "The stories below show how optional fields within this `advancedParameters` object that can be used to fine-tune the autosuggest data returned from constructor's servers.";
|
|
30
30
|
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.";
|
|
31
31
|
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.\n\nTo see this in action:\n1. Type \"pan\" in the example below.\n - Notice how the user is presented with a search term of \"all week flex pant\" as well as \"all week flex pant in Chinos\" and \"all week flex pant baby in Athleisure Pants & Joggers\"\n2. Navigate to the \"Terms With Group Suggestions\" story (using the navigation menu to the left)\n3. Then use the Controls to adjust the values of `numTermsWithGroupSuggestions` to `3` and `numGroupsSuggestedPerTerm` to `1`\n4. Next, type \"pan\" in the example autocomplete input field.\n - Notice how the user is presented with three different search terms that have a maximum of one \"in {group}\" suggestion each";
|
|
@@ -8,6 +8,7 @@ declare const useCioAutocomplete: (options: UseCioAutocompleteOptions) => {
|
|
|
8
8
|
featureToggles: {
|
|
9
9
|
featureDisplaySearchSuggestionImages: boolean;
|
|
10
10
|
featureDisplaySearchSuggestionResultCounts: boolean;
|
|
11
|
+
featureDisplayZeroStateRecommendations: boolean;
|
|
11
12
|
};
|
|
12
13
|
isOpen: boolean;
|
|
13
14
|
getMenuProps: () => any;
|
|
@@ -4,6 +4,6 @@ import { AdvancedParameters, UserDefinedSection, Section } from '../types';
|
|
|
4
4
|
export default function useSections(query: string, cioClient: Nullable<ConstructorIO>, sections: UserDefinedSection[], zeroStateSections: UserDefinedSection[] | undefined, advancedParameters?: AdvancedParameters): {
|
|
5
5
|
activeSections: UserDefinedSection[];
|
|
6
6
|
activeSectionsWithData: Section[];
|
|
7
|
-
zeroStateActiveSections:
|
|
7
|
+
zeroStateActiveSections: number | false | undefined;
|
|
8
8
|
request: Partial<import("@constructor-io/constructorio-client-javascript/lib/types").AutocompleteRequestType>;
|
|
9
9
|
};
|
package/lib/types/types.d.ts
CHANGED
package/lib/types/utils.d.ts
CHANGED
|
@@ -8,9 +8,10 @@ export type GetItemPosition = (args: {
|
|
|
8
8
|
index: number;
|
|
9
9
|
sectionId: string;
|
|
10
10
|
};
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function getFeatures(request: Partial<AutocompleteRequestType>): {
|
|
12
12
|
featureDisplaySearchSuggestionImages: boolean;
|
|
13
13
|
featureDisplaySearchSuggestionResultCounts: boolean;
|
|
14
|
+
featureDisplayZeroStateRecommendations: boolean;
|
|
14
15
|
};
|
|
15
16
|
export declare const getItemPosition: GetItemPosition;
|
|
16
17
|
type CamelToStartCase = (camelCaseString: string) => string;
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "1.20.
|
|
1
|
+
declare const _default: "1.20.3";
|
|
2
2
|
export default _default;
|
package/package.json
CHANGED