@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.
@@ -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) {
@@ -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-sectionName {
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: (0, utils_1.getSearchSuggestionFeatures)(request),
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 })), { className: `cio-item ${sectionItemTestId}`, 'data-testid': sectionItemTestId });
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 && openOnFocus !== false) {
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
- const { type } = section;
145
- let sectionTitle;
146
- // Add the indexSectionName as a class to the section container to make sure it gets the styles
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 indexSectionName = type !== 'custom' && section.indexSectionName ? (0, utils_1.toKebabCase)(section.indexSectionName) : '';
150
- switch (type) {
151
- case 'recommendations':
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: `${sectionTitle} cio-section ${indexSectionName}`,
179
+ className: `cio-section cio-section-${sectionListingType} ${getDeprecatedClassNames()}`,
166
180
  ref: section.ref,
167
181
  role: 'none',
168
- 'data-cnstrc-section': (_a = section.data[0]) === null || _a === void 0 ? void 0 : _a.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] = { displayName: pod.display_name, podId: 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.getSearchSuggestionFeatures = void 0;
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 getSearchSuggestionFeatures(request) {
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.getSearchSuggestionFeatures = getSearchSuggestionFeatures;
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));
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '1.20.1';
3
+ exports.default = '1.20.3';
@@ -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) {
@@ -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-sectionName {
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, getSearchSuggestionFeatures, trackRecommendationView, toKebabCase, } from '../utils';
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: getSearchSuggestionFeatures(request),
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 && openOnFocus !== false) {
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
- const { type } = section;
155
- let sectionTitle;
156
- // Add the indexSectionName as a class to the section container to make sure it gets the styles
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 indexSectionName = type !== 'custom' && section.indexSectionName ? toKebabCase(section.indexSectionName) : '';
160
- switch (type) {
161
- case 'recommendations':
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: `${sectionTitle} cio-section ${indexSectionName}`,
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] = { displayName: pod.display_name, podId: 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 getSearchSuggestionFeatures(request) {
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 }) => {
@@ -1 +1 @@
1
- export default '1.20.1';
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
- .cio-autocomplete .cio-sectionName {
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
- .cio-autocomplete .cio-item.cio-item-SearchSuggestions {
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-autocomplete .products {
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-autocomplete .products .cio-item {
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;
@@ -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-sectionName {\n margin: 5px 3px;\n}\n\n.cio-autocomplete.custom-autocomplete-styles .cio-results {\n width: 620px;\n max-height: 334px;\n overflow: hidden;\n border-radius: 0px 0px 8px 8px;\n color: rgb(51, 51, 51);\n}\n\n.cio-autocomplete.custom-autocomplete-styles .products p {\n padding: 5px 5px 0;\n}\n```\n";
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: false | UserDefinedSection[] | undefined;
7
+ zeroStateActiveSections: number | false | undefined;
8
8
  request: Partial<import("@constructor-io/constructorio-client-javascript/lib/types").AutocompleteRequestType>;
9
9
  };
@@ -137,4 +137,5 @@ export type Translations = {
137
137
  export interface PodData {
138
138
  podId: string;
139
139
  displayName: string;
140
+ request: Partial<AutocompleteRequestType>;
140
141
  }
@@ -8,9 +8,10 @@ export type GetItemPosition = (args: {
8
8
  index: number;
9
9
  sectionId: string;
10
10
  };
11
- export declare function getSearchSuggestionFeatures(request: Partial<AutocompleteRequestType>): {
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;
@@ -1,2 +1,2 @@
1
- declare const _default: "1.20.1";
1
+ declare const _default: "1.20.3";
2
2
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructor-io/constructorio-ui-autocomplete",
3
- "version": "1.20.1",
3
+ "version": "1.20.3",
4
4
  "description": "Constructor.io Autocomplete UI library for web applications",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",