Skip to content

Commit 12845d9

Browse files
committed
Merge branch 'main' into docs-revamp
2 parents 57c0878 + 10caae0 commit 12845d9

33 files changed

+417
-244
lines changed

.prettierrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"singleQuote": true,
66
"jsxBracketSameLine": true,
77
"jsxSingleQuote": true,
8-
"bracketSameLine": true
8+
"bracketSameLine": true,
9+
"trailingComma": "es5"
910
}

package-lock.json

+7-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/Components/CioAutocomplete/CioAutocomplete.test.tsx

+100-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-console */
22
import '@testing-library/jest-dom';
33
import React from 'react';
4-
import { render } from '@testing-library/react';
4+
import { render, screen, fireEvent } from '@testing-library/react';
55
import { CioAutocomplete } from '../../../src';
66
import { mockCioClientJS } from '../../test-utils';
77
import { apiKey as DEMO_API_KEY, onSubmitDefault as onSubmit } from '../../../src/constants';
@@ -126,4 +126,103 @@ describe('CioAutocomplete Client-Side Rendering', () => {
126126

127127
expect(console.error).not.toHaveBeenCalled();
128128
});
129+
describe('Custom UI variant', () => {
130+
afterEach(() => {
131+
window.sessionStorage.removeItem('_constructorio_custom_autosuggest_ui');
132+
});
133+
134+
it('Renders the default CioAutocomplete when no custom UI variant is set', async () => {
135+
render(<CioAutocomplete apiKey={DEMO_API_KEY} onSubmit={() => {}} />);
136+
137+
const searchInput = screen.getByRole('combobox');
138+
139+
fireEvent.change(searchInput, { target: { value: 'pants' } });
140+
141+
const options = (await screen.findAllByRole('option', undefined, { timeout: 5000 })).filter(
142+
(elem) => elem.getAttribute('data-cnstrc-item-section') === 'Search Suggestions'
143+
);
144+
145+
options.forEach((option) => {
146+
const suggestionCount = option.querySelector('.cio-suggestion-count');
147+
expect(suggestionCount).not.toBeInTheDocument();
148+
});
149+
});
150+
151+
it('Takes custom UI variant from session storage and applies features based on the variant', async () => {
152+
window.sessionStorage.setItem(
153+
'_constructorio_custom_autosuggest_ui',
154+
'custom_autosuggest_ui_result_count'
155+
);
156+
157+
const props = {
158+
apiKey: DEMO_API_KEY,
159+
onSubmit: () => {},
160+
sections: [
161+
{
162+
indexSectionName: 'Search Suggestions',
163+
numResults: 8,
164+
},
165+
{
166+
indexSectionName: 'Products',
167+
numResults: 6,
168+
},
169+
],
170+
};
171+
172+
render(<CioAutocomplete {...props} />);
173+
174+
const searchInput = screen.getByRole('combobox');
175+
176+
fireEvent.change(searchInput, { target: { value: 'pants' } });
177+
178+
const options = (await screen.findAllByRole('option')).filter(
179+
(elem) => elem.getAttribute('data-cnstrc-item-section') === 'Search Suggestions'
180+
);
181+
182+
options.forEach((option) => {
183+
const suggestionCount = option.querySelector('.cio-suggestion-count');
184+
expect(suggestionCount).toBeInTheDocument();
185+
});
186+
});
187+
188+
it('Applies features sent in advancedParameters even if a custom UI variant is set that affects that feature', async () => {
189+
window.sessionStorage.setItem(
190+
'_constructorio_custom_autosuggest_ui',
191+
'custom_autosuggest_ui_result_count'
192+
);
193+
194+
const props = {
195+
apiKey: DEMO_API_KEY,
196+
onSubmit: () => {},
197+
sections: [
198+
{
199+
indexSectionName: 'Search Suggestions',
200+
numResults: 8,
201+
},
202+
{
203+
indexSectionName: 'Products',
204+
numResults: 6,
205+
},
206+
],
207+
advancedParameters: {
208+
displaySearchSuggestionResultCounts: false,
209+
},
210+
};
211+
212+
render(<CioAutocomplete {...props} />);
213+
214+
const searchInput = screen.getByRole('combobox');
215+
216+
fireEvent.change(searchInput, { target: { value: 'pants' } });
217+
218+
const options = (await screen.findAllByRole('option')).filter(
219+
(elem) => elem.getAttribute('data-cnstrc-item-section') === 'Search Suggestions'
220+
);
221+
222+
options.forEach((option) => {
223+
const suggestionCount = option.querySelector('.cio-suggestion-count');
224+
expect(suggestionCount).not.toBeInTheDocument();
225+
});
226+
});
227+
});
129228
});

src/components/Autocomplete/AutocompleteResults/AutocompleteResults.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { ReactNode, useContext } from 'react';
22
import { Item, Section } from '../../../types';
3-
import { toKebabCase } from '../../../utils';
3+
import { toKebabCase } from '../../../utils/format';
44
import { CioAutocompleteContext } from '../CioAutocompleteProvider';
55
import SectionItemsList from '../SectionItemsList/SectionItemsList';
66
import CloseIcon from './CloseIcon';

src/components/Autocomplete/SectionItem/SectionItem.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CioAutocompleteContext } from '../CioAutocompleteProvider';
33
import { Item } from '../../../types';
44
import { isProduct, isInGroupSuggestion, isSearchSuggestion } from '../../../typeGuards';
55
import SectionItemText from './SectionItemText';
6-
import { translate } from '../../../utils';
6+
import { translate } from '../../../utils/helpers';
77
import SearchSuggestionItem from './SearchSuggestionItem';
88

99
export interface SectionItemProps {

src/components/Autocomplete/SectionItem/SectionItemText.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { Item } from '../../../types';
33
import { isInGroupSuggestion } from '../../../typeGuards';
4-
import { escapeRegExp } from '../../../utils';
4+
import { escapeRegExp } from '../../../utils/helpers';
55

66
export interface SectionItemTextProps {
77
item: Item;

src/components/Autocomplete/SectionItemsList/SectionItemsList.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import React, { ReactElement, useContext } from 'react';
22
import { Section } from '../../../types';
33
import SectionItem from '../SectionItem/SectionItem';
44
import CustomSectionItem from '../SectionItem/CustomSectionItem';
5-
import { camelToStartCase, translate } from '../../../utils';
5+
import { translate } from '../../../utils/helpers';
6+
import { camelToStartCase } from '../../../utils/format';
67
import { CioAutocompleteContext } from '../CioAutocompleteProvider';
78
import NoResults from '../AutocompleteResults/NoResults';
89

src/hooks/useCioAutocomplete.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,16 @@ import {
1212
UseCioAutocompleteOptions,
1313
} from '../types';
1414
import usePrevious from './usePrevious';
15-
import {
16-
getItemPosition,
17-
getItemsForActiveSections,
18-
getFeatures,
19-
trackRecommendationView,
20-
toKebabCase,
21-
trackSearchSubmit,
22-
} from '../utils';
15+
import { getItemPosition, getItemsForActiveSections } from '../utils/helpers';
16+
import { toKebabCase } from '../utils/format';
17+
import { trackRecommendationView, trackSearchSubmit } from '../utils/tracking';
18+
import { getFeatures } from '../utils/features';
2319
import useConsoleErrors from './useConsoleErrors';
2420
import useSections from './useSections';
2521
import useRecommendationsObserver from './useRecommendationsObserver';
2622
import { isCustomSection, isRecommendationsSection } from '../typeGuards';
2723
import useNormalizedProps from './useNormalizedProps';
24+
import useCustomBlur from './useCustomBlur';
2825

2926
export const defaultSections: UserDefinedSection[] = [
3027
{
@@ -92,6 +89,8 @@ const useCioAutocomplete = (options: UseCioAutocompleteOptions) => {
9289
...rest,
9390
});
9491

92+
useCustomBlur(isOpen, closeMenu, autocompleteClassName);
93+
9594
// Log console errors
9695
useConsoleErrors(sections, activeSections);
9796

src/hooks/useCioClient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMemo } from 'react';
22
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
33
import { Nullable } from '@constructor-io/constructorio-client-javascript/lib/types';
4-
import { getCioClient } from '../utils';
4+
import { getCioClient } from '../utils/helpers';
55
import { CioClientConfig } from '../types';
66

77
type UseCioClient = (cioClientConfig: CioClientConfig) => Nullable<ConstructorIOClient>;

src/hooks/useCustomBlur.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { useCallback, useEffect } from 'react';
2+
3+
const useCustomBlur = (isOpen: boolean, closeMenu: () => void, autocompleteClassName: string) => {
4+
const handleDocumentClick = useCallback(
5+
(event: MouseEvent) => {
6+
const classNames = autocompleteClassName.split(' ').join('.');
7+
if (isOpen && !(event.target as HTMLElement)?.closest(`.${classNames}`)) {
8+
closeMenu();
9+
}
10+
},
11+
[closeMenu, isOpen, autocompleteClassName]
12+
);
13+
14+
useEffect(() => {
15+
document.addEventListener('mousedown', handleDocumentClick);
16+
return () => {
17+
document.removeEventListener('mousedown', handleDocumentClick);
18+
};
19+
}, [handleDocumentClick]);
20+
};
21+
22+
export default useCustomBlur;

src/hooks/useDownShift.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useCombobox, UseComboboxProps, UseComboboxReturnValue } from 'downshift
22
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
33
import { Nullable } from '@constructor-io/constructorio-client-javascript/lib/types';
44
import { Item, OnSubmit } from '../types';
5-
import { trackSearchSubmit, trackAutocompleteSelect } from '../utils';
5+
import { trackSearchSubmit, trackAutocompleteSelect } from '../utils/tracking';
66

77
let idCounter = 0;
88

@@ -69,6 +69,15 @@ const useDownShift: UseDownShift = ({
6969
}
7070
}
7171
},
72+
stateReducer: (state, actionAndChanges) => {
73+
const { type, changes } = actionAndChanges;
74+
75+
// Override dropdown close on blur
76+
if (type === useCombobox.stateChangeTypes.InputBlur) {
77+
return { ...changes, isOpen: state.isOpen };
78+
}
79+
return changes;
80+
},
7281
...rest,
7382
});
7483

src/hooks/useSections/useActiveSectionsWithData.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-params */
22
import { RefObject, useEffect, useState } from 'react';
33
import { UserDefinedSection, Section, SectionsData, PodData } from '../../types';
4-
import { getActiveSectionsWithData } from '../../utils';
4+
import { getActiveSectionsWithData } from '../../utils/helpers';
55

66
export default function useActiveSectionsWithData(
77
sectionsResults: SectionsData,

src/hooks/useSections/useRemoveSections.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-params */
22
import { useEffect } from 'react';
33
import { PodData, UserDefinedSection } from '../../types';
4-
import { getFeatures } from '../../utils';
4+
import { getFeatures } from '../../utils/features';
55

66
export default function useRemoveSections(
77
sections: UserDefinedSection[],

src/stories/Autocomplete/Component/AdvancedParameters.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CioAutocomplete } from '../../../index';
22
import { argTypes } from '../argTypes';
3-
import { stringifyWithDefaults } from '../../../utils';
3+
import { stringifyWithDefaults } from '../../../utils/format';
44
import { ComponentTemplate, addComponentStoryDescription } from '.';
55
import {
66
advancedParametersDescription,

src/stories/Autocomplete/Component/Sections.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { CioAutocomplete } from '../../../index';
33
import { argTypes } from '../argTypes';
4-
import { stringifyWithDefaults } from '../../../utils';
4+
import { stringifyWithDefaults } from '../../../utils/format';
55
import {
66
contentDescription,
77
customSectionDescription,

src/stories/Autocomplete/Component/UserEvents.stories.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/* eslint-disable no-console */
22
import { CioAutocomplete } from '../../../index';
33
import { argTypes } from '../argTypes';
4-
import { stringifyWithDefaults, disableStoryActions, functionStrings } from '../../../utils';
4+
import { stringifyWithDefaults } from '../../../utils/format';
5+
import { disableStoryActions, functionStrings } from '../../../utils/helpers';
56
import {
67
onChangeDescription,
78
onFocusDescription,

src/stories/Autocomplete/Component/ZeroState.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CioAutocomplete } from '../../../index';
22
import { argTypes } from '../argTypes';
3-
import { stringifyWithDefaults } from '../../../utils';
3+
import { stringifyWithDefaults } from '../../../utils/format';
44
import {
55
customSectionDescription,
66
multipleSectionsDescription,

src/stories/Autocomplete/Component/index.stories.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { Meta, StoryObj } from '@storybook/react';
22
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
33
import { CioAutocomplete, CioAutocompleteProps } from '../../../index';
4-
import { functionStrings, stringifyWithDefaults } from '../../../utils';
54
import { ComponentTemplate, FullExampleTemplate, addComponentStoryDescription } from '.';
65
import {
76
fullFeaturedAndStyledExampleDescription,
@@ -14,6 +13,8 @@ import {
1413
onSubmitDefault as onSubmit,
1514
cioJsClientOptionsDescription,
1615
} from '../../../constants';
16+
import { stringifyWithDefaults } from '../../../utils/format';
17+
import { functionStrings } from '../../../utils/helpers';
1718

1819
const meta: Meta<typeof CioAutocomplete> = {
1920
title: 'Autocomplete/Component',

src/stories/Autocomplete/Component/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { CioAutocomplete } from '../../../index';
33
import { CioAutocompleteProps } from '../../../types';
4-
import { getStoryParams } from '../../../utils';
4+
import { getStoryParams } from '../../../utils/helpers';
55

66
export function ComponentTemplate(args: CioAutocompleteProps) {
77
return <CioAutocomplete {...args} />;

src/stories/Autocomplete/Hook/AdvancedParameters.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CioAutocomplete } from '../../../index';
22
import { argTypes } from '../argTypes';
3-
import { stringifyWithDefaults } from '../../../utils';
3+
import { stringifyWithDefaults } from '../../../utils/format';
44
import { HooksTemplate, addHookStoryCode } from '.';
55
import {
66
advancedParametersDescription,

src/stories/Autocomplete/Hook/Sections.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CioAutocomplete } from '../../../index';
22
import { argTypes } from '../argTypes';
3-
import { stringifyWithDefaults } from '../../../utils';
3+
import { stringifyWithDefaults } from '../../../utils/format';
44
import {
55
contentDescription,
66
customSectionDescription,

src/stories/Autocomplete/Hook/UserEvents.stories.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/* eslint-disable no-console */
22
import { CioAutocomplete } from '../../../index';
33
import { argTypes } from '../argTypes';
4-
import { stringifyWithDefaults, disableStoryActions, functionStrings } from '../../../utils';
4+
import { stringifyWithDefaults } from '../../../utils/format';
5+
import { disableStoryActions, functionStrings } from '../../../utils/helpers';
56
import {
67
onChangeDescription,
78
onFocusDescription,

src/stories/Autocomplete/Hook/ZeroState.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CioAutocomplete } from '../../../index';
22
import { argTypes } from '../argTypes';
3-
import { stringifyWithDefaults } from '../../../utils';
3+
import { stringifyWithDefaults } from '../../../utils/format';
44
import {
55
customSectionDescription,
66
multipleSectionsDescription,

src/stories/Autocomplete/Hook/index.stories.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
22
import { CioAutocomplete } from '../../../index';
33
import { argTypes } from '../argTypes';
4-
import { functionStrings, stringifyWithDefaults } from '../../../utils';
4+
import { stringifyWithDefaults } from '../../../utils/format';
5+
import { functionStrings } from '../../../utils/helpers';
56
import { HooksTemplate, addHookStoryCode } from '.';
67
import {
78
apiKeyDescription,

0 commit comments

Comments
 (0)