Skip to content

Commit 1f56823

Browse files
martinrajdlMudaafi
andauthored
[CSL-3088] - fix issue with conflicting styles (#128)
* fix issue with conflicting styles * kebab case section name class * Render the same classnames in Hooks Stories * Rename sectionTitle * Update tests * bugfix * Update code sample * address comments * Update hooks stories to match component stories * Add backwards compatibility * Adds fallback styles --------- Co-authored-by: Ahmad Mudaafi <ahmad.mudaafi@constructor.io>
1 parent cdca619 commit 1f56823

File tree

10 files changed

+135
-65
lines changed

10 files changed

+135
-65
lines changed

.storybook/custom-styles-story.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
right: 24px;
1818
}
1919

20-
.cio-autocomplete.custom-autocomplete-styles .cio-sectionName {
20+
.cio-autocomplete.custom-autocomplete-styles .cio-section-name {
2121
margin: 5px 3px;
2222
}
2323

.storybook/full-example-styles-story.css

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
right: 0;
5858
}
5959

60-
.cio-autocomplete.full-example-autocomplete-styles .cio-sectionName {
60+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-name {
6161
margin: 5px 3px;
6262
}
6363

@@ -72,25 +72,25 @@
7272
color: rgb(70, 70, 70);
7373
}
7474

75-
.cio-autocomplete.full-example-autocomplete-styles .products.cio-section {
75+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-products {
7676
padding: 0px;
7777
}
7878

79-
.cio-autocomplete.full-example-autocomplete-styles .products .cio-item-Products {
79+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-products .cio-item {
8080
width: 120px !important;
8181
height: fit-content;
8282
padding: 15px;
8383
}
8484

85-
.cio-autocomplete.full-example-autocomplete-styles .products .cio-section-items {
85+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-products .cio-section-items {
8686
text-align: center;
8787
}
8888

89-
.cio-autocomplete.full-example-autocomplete-styles .products .cio-product-text {
89+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-products .cio-product-text {
9090
padding: 15px 5px 0;
9191
}
9292

93-
.cio-autocomplete.full-example-autocomplete-styles .products .cio-item .cio-product-image {
93+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-products .cio-item .cio-product-image {
9494
width: 100%;
9595
height: 100%;
9696
min-height: 200px;
@@ -104,7 +104,7 @@
104104
border-radius: 0px;
105105
}
106106

107-
.cio-autocomplete.full-example-autocomplete-styles .products p {
107+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-products p {
108108
padding: 5px 5px 0;
109109
}
110110

@@ -125,7 +125,7 @@
125125
width: unset;
126126
}
127127

128-
.cio-autocomplete.full-example-autocomplete-styles .Products.cio-section {
128+
.cio-autocomplete.full-example-autocomplete-styles .cio-section-products {
129129
display: none;
130130
}
131131
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function YourComponent() {
8080
{sections?.map((section) => (
8181
<div key={section.indexSectionName} className={section.indexSectionName}>
8282
<div className='cio-section'>
83-
<div className='cio-sectionName'>
83+
<div className='cio-section-name'>
8484
{section?.displayName || section.indexSectionName}
8585
</div>
8686
<div className='cio-items'>

src/components/Autocomplete/SectionItemsList/SectionItemsList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ const DefaultRenderSectionItemsList: RenderSectionItemsList = function ({ sectio
3939

4040
if (!section?.data?.length) return null;
4141

42+
// @deprecated `cio-sectionName` will be removed in the next major release
4243
return (
4344
<li {...getSectionProps(section)}>
44-
<h5 className='cio-sectionName' aria-hidden>
45+
<h5 className='cio-section-name cio-sectionName' aria-hidden>
4546
{camelToStartCase(sectionTitle)}
4647
</h5>
4748
<ul className='cio-section-items' role='none'>

src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ import '@constructor-io/constructorio-ui-autocomplete/styles.css';
191191
right: 24px;
192192
}
193193
194-
.cio-autocomplete.custom-autocomplete-styles .cio-sectionName {
194+
.cio-autocomplete.custom-autocomplete-styles .cio-section-name {
195195
margin: 5px 3px;
196196
}
197197

src/hooks/useCioAutocomplete.ts

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
import useConsoleErrors from './useConsoleErrors';
2222
import useSections from './useSections';
2323
import useRecommendationsObserver from './useRecommendationsObserver';
24-
import { isAutocompleteSection, isRecommendationsSection } from '../typeGuards';
24+
import { isAutocompleteSection, isCustomSection, isRecommendationsSection } from '../typeGuards';
2525

2626
export const defaultSections: UserDefinedSection[] = [
2727
{
@@ -146,6 +146,7 @@ const useCioAutocomplete = (options: UseCioAutocompleteOptions) => {
146146

147147
return {
148148
...getItemProps({ item, index }),
149+
// @deprecated `sectionItemTestId` will be removed as a className in the next major version
149150
className: `cio-item ${sectionItemTestId}`,
150151
'data-testid': sectionItemTestId,
151152
};
@@ -218,37 +219,53 @@ const useCioAutocomplete = (options: UseCioAutocompleteOptions) => {
218219
'data-testid': 'cio-form',
219220
}),
220221
getSectionProps: (section: Section) => {
221-
const { type } = section;
222-
let sectionTitle: string;
222+
// @deprecated ClassNames derived from this fn will be removed in the next major version
223+
const getDeprecatedClassNames = () => {
224+
const { type } = section;
225+
let sectionTitle: string;
223226

224-
// Add the indexSectionName as a class to the section container to make sure it gets the styles
227+
const indexSectionName =
228+
type !== 'custom' && section.indexSectionName
229+
? toKebabCase(section.indexSectionName)
230+
: '';
231+
232+
switch (type) {
233+
case 'recommendations':
234+
sectionTitle = section.podId;
235+
break;
236+
case 'autocomplete':
237+
sectionTitle = section.displayName || section.indexSectionName;
238+
break;
239+
case 'custom':
240+
sectionTitle = section.displayName;
241+
break;
242+
default:
243+
sectionTitle = section.displayName || section.indexSectionName;
244+
break;
245+
}
246+
247+
return `${sectionTitle} ${indexSectionName}`;
248+
};
249+
250+
// Always add the indexSectionName (defaults to Products) as a class to the section container for the styles
225251
// Even if the section is a recommendation pod, if the results are "Products" or "Search Suggestions"
226252
// ... they should be styled accordingly
227-
const indexSectionName =
228-
type !== 'custom' && section.indexSectionName ? toKebabCase(section.indexSectionName) : '';
229-
230-
switch (type) {
231-
case 'recommendations':
232-
sectionTitle = section.podId;
233-
break;
234-
case 'autocomplete':
235-
sectionTitle = section.displayName || section.indexSectionName;
236-
break;
237-
case 'custom':
238-
sectionTitle = section.displayName;
239-
break;
240-
default:
241-
sectionTitle = section.displayName || section.indexSectionName;
242-
break;
243-
}
253+
const sectionListingType = isCustomSection(section)
254+
? 'custom'
255+
: toKebabCase(section.indexSectionName || section.data[0]?.section || 'Products');
244256

245257
const attributes: HTMLPropsWithCioDataAttributes = {
246-
className: `${sectionTitle} cio-section ${indexSectionName}`,
258+
className: `cio-section cio-section-${sectionListingType} ${getDeprecatedClassNames()}`,
247259
ref: section.ref,
248260
role: 'none',
249261
'data-cnstrc-section': section.data[0]?.section,
250262
};
251263

264+
if (isCustomSection(section)) {
265+
attributes['data-cnstrc-custom-section'] = true;
266+
attributes['data-cnstrc-custom-section-name'] = section.displayName;
267+
}
268+
252269
// Add data attributes for recommendations
253270
if (isRecommendationsSection(section)) {
254271
attributes['data-cnstrc-recommendations'] = true;

src/stories/Autocomplete/Hook/index.tsx

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/* eslint-disable no-param-reassign */
22
import React from 'react';
33
import useCioAutocomplete from '../../../hooks/useCioAutocomplete';
4-
import { isRecommendationsSection } from '../../../typeGuards';
4+
import { isCustomSection, isRecommendationsSection } from '../../../typeGuards';
55
import { Item } from '../../../types';
6-
import { getStoryParams, toKebabCase } from '../../../utils';
6+
import { camelToStartCase, getStoryParams, toKebabCase } from '../../../utils';
77

88
export function HooksTemplate(args) {
99
const {
@@ -119,32 +119,41 @@ export function HooksTemplate(args) {
119119
if (!section?.data?.length) {
120120
return null;
121121
}
122+
122123
const { type, displayName } = section;
123-
let sectionName = section.displayName;
124+
const sectionListingType = isCustomSection(section)
125+
? 'custom'
126+
: toKebabCase(section.indexSectionName || section.data[0]?.section || 'Products');
124127

128+
let sectionTitle: string;
125129
switch (type) {
126130
case 'recommendations':
127-
sectionName = section.podId;
131+
sectionTitle = section.podId;
128132
break;
129133
case 'custom':
130-
sectionName = toKebabCase(displayName);
134+
sectionTitle = displayName;
131135
break;
132136
case 'autocomplete':
133-
sectionName = section.indexSectionName;
137+
sectionTitle = section.indexSectionName;
134138
break;
135139
default:
136-
sectionName = section.indexSectionName;
140+
sectionTitle = section.indexSectionName;
137141
break;
138142
}
139143

140-
const recommendationsSection = isRecommendationsSection(section)
141-
? section.indexSectionName
142-
: '';
144+
if (displayName) {
145+
sectionTitle = displayName;
146+
}
147+
148+
let sectionClassNames = toKebabCase(sectionListingType);
149+
if (isRecommendationsSection(section)) {
150+
sectionClassNames += ` ${toKebabCase(section.podId)}`;
151+
}
143152

144153
return (
145-
<div key={sectionName} className={`${sectionName} ${recommendationsSection}`}>
154+
<div key={sectionTitle} className={sectionClassNames}>
146155
<div {...getSectionProps(section)}>
147-
<h5 className='cio-sectionName'>{section.displayName || sectionName}</h5>
156+
<h5 className='cio-section-name'>{camelToStartCase(sectionTitle)}</h5>
148157
<div className='cio-section-items'>
149158
{section?.data?.map((item) => renderItem(item))}
150159
</div>
@@ -271,32 +280,38 @@ function YourComponent() {
271280
if (!section?.data?.length) {
272281
return null;
273282
}
283+
274284
const { type, displayName } = section;
275-
let sectionName = section.displayName;
285+
let sectionTitle: string;
276286
277287
switch (type) {
278288
case 'recommendations':
279-
sectionName = section.podId;
289+
sectionTitle = section.podId;
280290
break;
281291
case 'custom':
282-
sectionName = toKebabCase(displayName);
292+
sectionTitle = displayName;
283293
break;
284294
case 'autocomplete':
285-
sectionName = section.indexSectionName;
295+
sectionTitle = section.indexSectionName;
286296
break;
287297
default:
288-
sectionName = section.indexSectionName;
298+
sectionTitle = section.indexSectionName;
289299
break;
290300
}
291301
292-
const recommendationsSection = isRecommendationsSection(section)
293-
? section.indexSectionName
294-
: '';
302+
if (displayName) {
303+
sectionTitle = displayName;
304+
}
305+
306+
let sectionClassNames = toKebabCase(sectionTitle);
307+
if (isRecommendationsSection(section)) {
308+
sectionClassNames += \` \${toKebabCase(section.indexSectionName)}\`;
309+
}
295310
296311
return (
297-
<div key={sectionName} className={\`\${sectionName} \${recommendationsSection}\`}>
312+
<div key={sectionTitle} className={sectionClassNames}>
298313
<div {...getSectionProps(section)}>
299-
<h5 className='cio-sectionName'>{sectionName}</h5>
314+
<h5 className='cio-section-name'>{camelToStartCase(sectionTitle)}</h5>
300315
<div className='cio-section-items'>
301316
{section?.data?.map((item) => renderItem(item))}
302317
</div>

src/stories/tests/ComponentTests.stories.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,19 @@ TypeSearchTermRenderSectionsDefaultOrder.play = async ({ canvasElement }) => {
223223
expect(canvas.getAllByTestId('cio-item-Products').length).toBeGreaterThan(0);
224224
expect(canvas.getAllByText('Best Sellers').length).toBeGreaterThan(0);
225225

226+
expect(canvas.getByTestId('cio-results').children[0].className).toContain('cio-section');
227+
expect(canvas.getByTestId('cio-results').children[0].className).toContain(
228+
'cio-section-search-suggestions'
229+
);
230+
231+
expect(canvas.getByTestId('cio-results').children[1].className).toContain('cio-section');
232+
expect(canvas.getByTestId('cio-results').children[1].className).toContain('cio-section-products');
233+
234+
// bestsellers indexSectionName is products, and we render class based on that
235+
expect(canvas.getByTestId('cio-results').children[2].className).toContain('cio-section');
236+
expect(canvas.getByTestId('cio-results').children[2].className).toContain('cio-section-products');
237+
238+
// @deprecated The following classNames will be removed in the next major version
226239
expect(canvas.getByTestId('cio-results').children[0].className).toContain('Search Suggestions');
227240
expect(canvas.getByTestId('cio-results').children[1].className).toContain('Products');
228241
expect(canvas.getByTestId('cio-results').children[2].className).toContain('bestsellers');
@@ -254,6 +267,19 @@ TypeSearchTermRenderSectionsCustomOrder.play = async ({ canvasElement }) => {
254267
expect(canvas.getAllByTestId('cio-item-Products').length).toBeGreaterThan(0);
255268
expect(canvas.getAllByText('Best Sellers').length).toBeGreaterThan(0);
256269

270+
expect(canvas.getByTestId('cio-results').children[0].className).toContain('cio-section');
271+
expect(canvas.getByTestId('cio-results').children[0].className).toContain('cio-section-products');
272+
273+
// bestsellers indexSectionName is products, and we render class based on that
274+
expect(canvas.getByTestId('cio-results').children[1].className).toContain('cio-section');
275+
expect(canvas.getByTestId('cio-results').children[1].className).toContain('cio-section-products');
276+
277+
expect(canvas.getByTestId('cio-results').children[2].className).toContain('cio-section');
278+
expect(canvas.getByTestId('cio-results').children[2].className).toContain(
279+
'cio-section-search-suggestions'
280+
);
281+
282+
// @deprecated The following classNames will be removed in the next major version
257283
expect(canvas.getByTestId('cio-results').children[0].className).toContain('Products');
258284
expect(canvas.getByTestId('cio-results').children[1].className).toContain('bestsellers');
259285
expect(canvas.getByTestId('cio-results').children[2].className).toContain('Search Suggestions');

src/stories/tests/HooksTests.stories.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,10 @@ TypeSearchTermRenderSectionsDefaultOrder.play = async ({ canvasElement }) => {
217217
expect(canvas.getAllByTestId('cio-item-Products').length).toBeGreaterThan(0);
218218
expect(canvas.getAllByText('Best Sellers').length).toBeGreaterThan(0);
219219

220-
expect(canvas.getByTestId('cio-results').children[0].className).toContain('Search Suggestions');
221-
expect(canvas.getByTestId('cio-results').children[1].className).toContain('Products');
220+
expect(canvas.getByTestId('cio-results').children[0].className).toContain('search-suggestions');
221+
expect(canvas.getByTestId('cio-results').children[1].className).toContain('products');
222222
expect(canvas.getByTestId('cio-results').children[2].className).toContain('bestsellers');
223+
expect(canvas.getByTestId('cio-results').children[2].className).toContain('products');
223224
};
224225

225226
// - type search term => render all sections in custom order
@@ -239,6 +240,7 @@ TypeSearchTermRenderSectionsCustomOrder.args = {
239240
},
240241
],
241242
};
243+
242244
TypeSearchTermRenderSectionsCustomOrder.play = async ({ canvasElement }) => {
243245
const canvas = within(canvasElement);
244246
await userEvent.type(canvas.getByTestId('cio-input'), 'red', { delay: 100 });
@@ -248,9 +250,10 @@ TypeSearchTermRenderSectionsCustomOrder.play = async ({ canvasElement }) => {
248250
expect(canvas.getAllByTestId('cio-item-Products').length).toBeGreaterThan(0);
249251
expect(canvas.getAllByText('Best Sellers').length).toBeGreaterThan(0);
250252

251-
expect(canvas.getByTestId('cio-results').children[0].className).toContain('Products');
253+
expect(canvas.getByTestId('cio-results').children[0].className).toContain('products');
252254
expect(canvas.getByTestId('cio-results').children[1].className).toContain('bestsellers');
253-
expect(canvas.getByTestId('cio-results').children[2].className).toContain('Search Suggestions');
255+
expect(canvas.getByTestId('cio-results').children[1].className).toContain('products');
256+
expect(canvas.getByTestId('cio-results').children[2].className).toContain('search-suggestions');
254257
};
255258

256259
// - select term suggestion => network tracking event

0 commit comments

Comments
 (0)