Skip to content

Commit 44f6bc0

Browse files
authored
feat(search):ES-5892 Introduce displayName and displayKey fields to facets (#2690)
* feat(search):ES-5892 Introduce displayName and displayKey fields to facets * feat(search): ES-5892 Add displayName and filterKey fields to facets for improved labeling * feat(search): Enhance facet filters with displayName and filterKey fields for improved UI labeling and functionality * test(search):ES-5892 Refactor Facets Tests
1 parent c22f0e8 commit 44f6bc0

File tree

4 files changed

+109
-8
lines changed

4 files changed

+109
-8
lines changed

.changeset/facets-displayName.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@bigcommerce/catalyst-core": minor
3+
---
4+
5+
Introduce displayName and displayKey fields to facets for improved labeling and filtering
6+
7+
Facet filters now use the `displayName` field for more descriptive labels in the UI, replacing the deprecated `name` field. Product attribute facets now support the `filterKey` field for consistent parameter naming. The facet transformer has been updated to use `displayName` with a fallback to `filterName` when `displayName` is not available.

core/app/[locale]/(default)/(faceted)/fetch-faceted-search.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ const GetProductSearchResultsQuery = graphql(
3939
edges {
4040
node {
4141
__typename
42-
name
42+
displayName
4343
isCollapsedByDefault
4444
... on BrandSearchFilter {
4545
displayProductCount
46+
displayName
4647
brands {
4748
pageInfo {
4849
...PaginationFragment
@@ -60,6 +61,7 @@ const GetProductSearchResultsQuery = graphql(
6061
}
6162
... on CategorySearchFilter {
6263
displayProductCount
64+
displayName
6365
categories {
6466
pageInfo {
6567
...PaginationFragment
@@ -92,6 +94,8 @@ const GetProductSearchResultsQuery = graphql(
9294
... on ProductAttributeSearchFilter {
9395
displayProductCount
9496
filterName
97+
filterKey
98+
displayName
9599
attributes {
96100
pageInfo {
97101
...PaginationFragment
@@ -107,6 +111,7 @@ const GetProductSearchResultsQuery = graphql(
107111
}
108112
}
109113
... on RatingSearchFilter {
114+
displayName
110115
ratings {
111116
pageInfo {
112117
...PaginationFragment
@@ -122,6 +127,7 @@ const GetProductSearchResultsQuery = graphql(
122127
}
123128
}
124129
... on PriceSearchFilter {
130+
displayName
125131
selected {
126132
minPrice
127133
maxPrice

core/data-transformers/facets-transformer.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const facetsTransformer = async ({
2222
const { filters } = PublicToPrivateParams.parse(searchParams);
2323

2424
return allFacets.map((facet) => {
25-
const refinedFacet = refinedFacets.find((f) => f.name === facet.name);
25+
const refinedFacet = refinedFacets.find((f) => f.displayName === facet.displayName);
2626

2727
if (facet.__typename === 'CategorySearchFilter') {
2828
const refinedCategorySearchFilter =
@@ -31,7 +31,7 @@ export const facetsTransformer = async ({
3131
return {
3232
type: 'toggle-group' as const,
3333
paramName: 'categoryIn',
34-
label: facet.name,
34+
label: facet.displayName,
3535
defaultCollapsed: facet.isCollapsedByDefault,
3636
options: facet.categories.map((category) => {
3737
const refinedCategory = refinedCategorySearchFilter?.categories.find(
@@ -57,7 +57,7 @@ export const facetsTransformer = async ({
5757
return {
5858
type: 'toggle-group' as const,
5959
paramName: 'brand',
60-
label: facet.name,
60+
label: facet.displayName,
6161
defaultCollapsed: facet.isCollapsedByDefault,
6262
options: facet.brands.map((brand) => {
6363
const refinedBrand = refinedBrandSearchFilter?.brands.find(
@@ -80,8 +80,8 @@ export const facetsTransformer = async ({
8080

8181
return {
8282
type: 'toggle-group' as const,
83-
paramName: `attr_${facet.filterName}`,
84-
label: facet.filterName,
83+
paramName: `attr_${facet.filterKey}`,
84+
label: facet.displayName,
8585
defaultCollapsed: facet.isCollapsedByDefault,
8686
options: facet.attributes.map((attribute) => {
8787
const refinedAttribute = refinedProductAttributeSearchFilter?.attributes.find(
@@ -111,7 +111,7 @@ export const facetsTransformer = async ({
111111
return {
112112
type: 'rating' as const,
113113
paramName: 'minRating',
114-
label: facet.name,
114+
label: facet.displayName,
115115
disabled: refinedRatingSearchFilter == null && !isSelected,
116116
defaultCollapsed: facet.isCollapsedByDefault,
117117
};
@@ -126,7 +126,7 @@ export const facetsTransformer = async ({
126126
type: 'range' as const,
127127
minParamName: 'minPrice',
128128
maxParamName: 'maxPrice',
129-
label: facet.name,
129+
label: facet.displayName,
130130
min: facet.selected?.minPrice ?? undefined,
131131
max: facet.selected?.maxPrice ?? undefined,
132132
disabled: refinedPriceSearchFilter == null && !isSelected,

core/tests/ui/e2e/facets.spec.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { expect, Page, test } from '~/tests/fixtures';
2+
3+
const SHOP_ALL_URL = '/shop-all/';
4+
5+
const PRODUCT_LE_PARFAIT_JAR = '[Sample] 1 L Le Parfait Jar';
6+
const PRODUCT_DUSTPAN_BRUSH = '[Sample] Dustpan & Brush';
7+
const PRODUCT_UTILITY_CADDY = '[Sample] Utility Caddy';
8+
9+
async function expandFilterIfNeeded(page: Page, filterLabel: string) {
10+
const filterButton = page
11+
.getByRole('heading', { name: filterLabel, level: 3 })
12+
.getByRole('button', { name: filterLabel });
13+
14+
const isExpanded = await filterButton.getAttribute('aria-expanded');
15+
16+
if (isExpanded === 'false') {
17+
await filterButton.click();
18+
}
19+
}
20+
21+
async function clickSpecificFilterOption(page: Page, filterName: string, optionName: string) {
22+
const filterButton = page
23+
.getByRole('region', { name: filterName })
24+
.getByRole('button', { name: optionName, disabled: false });
25+
26+
await filterButton.click();
27+
}
28+
29+
test('Blue color filter shows expected product on shop-all page', async ({ page }) => {
30+
await page.goto(SHOP_ALL_URL);
31+
await expect(page.getByRole('heading', { name: 'Shop All 13' })).toBeVisible();
32+
33+
await expandFilterIfNeeded(page, 'Color');
34+
await clickSpecificFilterOption(page, 'Color', 'Blue');
35+
36+
await expect(page).toHaveURL((url) => url.searchParams.get('attr_Color') === 'Blue');
37+
await expect(page.getByRole('link', { name: PRODUCT_LE_PARFAIT_JAR })).toBeVisible();
38+
});
39+
40+
test('Brand filter shows correct products', async ({ page }) => {
41+
await page.goto(SHOP_ALL_URL);
42+
await expect(page.getByRole('heading', { name: 'Shop All 13' })).toBeVisible();
43+
44+
await expandFilterIfNeeded(page, 'Brand');
45+
await clickSpecificFilterOption(page, 'Brand', 'OFS');
46+
47+
await expect(page).toHaveURL((url) => url.searchParams.has('brand'));
48+
await expect(page.getByRole('heading', { name: 'Shop All 5' })).toBeVisible();
49+
await expect(page.getByRole('link', { name: PRODUCT_DUSTPAN_BRUSH })).toBeVisible();
50+
await expect(page.getByRole('link', { name: PRODUCT_UTILITY_CADDY })).toBeVisible();
51+
await expect(page.getByRole('link', { name: PRODUCT_LE_PARFAIT_JAR })).toBeVisible();
52+
});
53+
54+
test('Multiple filters work together (Color + Brand)', async ({ page }) => {
55+
await page.goto(SHOP_ALL_URL);
56+
await expect(page.getByRole('heading', { name: 'Shop All 13' })).toBeVisible();
57+
58+
await expandFilterIfNeeded(page, 'Color');
59+
await clickSpecificFilterOption(page, 'Color', 'Blue');
60+
61+
await expect(page).toHaveURL((url) => url.searchParams.get('attr_Color') === 'Blue');
62+
63+
await expandFilterIfNeeded(page, 'Brand');
64+
await clickSpecificFilterOption(page, 'Brand', 'OFS');
65+
66+
await expect(page).toHaveURL((url) => {
67+
const params = url.searchParams;
68+
69+
return params.get('attr_Color') === 'Blue' && params.has('brand');
70+
});
71+
await expect(page.getByRole('link', { name: PRODUCT_LE_PARFAIT_JAR })).toBeVisible();
72+
});
73+
74+
test('Removing filter restores product list', async ({ page }) => {
75+
await page.goto(SHOP_ALL_URL);
76+
await expect(page.getByRole('heading', { name: 'Shop All 13' })).toBeVisible();
77+
78+
await expandFilterIfNeeded(page, 'Brand');
79+
await clickSpecificFilterOption(page, 'Brand', 'OFS');
80+
81+
await expect(page).toHaveURL((url) => url.searchParams.has('brand'));
82+
await expect(page.getByRole('heading', { name: 'Shop All 5' })).toBeVisible();
83+
84+
await page.getByRole('button', { name: 'Reset filters' }).click();
85+
86+
await expect(page).toHaveURL((url) => !url.searchParams.has('brand'));
87+
await expect(page.getByRole('heading', { name: 'Shop All 13' })).toBeVisible();
88+
});

0 commit comments

Comments
 (0)