From 564cab6f2f5c9da25f1478e8bc7f2ad3d2809ece Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 12 Feb 2026 19:34:11 +0530 Subject: [PATCH 01/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index e3577b7db8..2e7427effb 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -257,8 +257,11 @@ function getFilterFromParams(filterParam) { attribute, in: value.split(commaRegex), }); - } else if (value.includes('-')) { - // Handle range values (like price) + } + + const rangeRegex = /^\d+(\.\d+)?-\d+(\.\d+)?$/; + + if (rangeRegex.test(value)) { const [from, to] = value.split('-'); results.push({ attribute, @@ -268,7 +271,7 @@ function getFilterFromParams(filterParam) { }, }); } else { - // Handle single values (like categories with one value) + // Supports hyphens in category/url keys results.push({ attribute, in: [value], From b887ab3637cdb705cff8acab445beb8dac4eb8c6 Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 12 Feb 2026 20:18:36 +0530 Subject: [PATCH 02/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index 2e7427effb..c0002e9e7a 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -257,7 +257,7 @@ function getFilterFromParams(filterParam) { attribute, in: value.split(commaRegex), }); - } + } const rangeRegex = /^\d+(\.\d+)?-\d+(\.\d+)?$/; From 5f251bbd3b13a14bac86c313122f438029e8181c Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 12 Feb 2026 19:34:11 +0530 Subject: [PATCH 03/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index 06d57a503a..a57cf8f92c 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -252,8 +252,11 @@ function getFilterFromParams(filterParam) { attribute, in: value.split(commaRegex), }); - } else if (value.includes('-')) { - // Handle range values (like price) + } + + const rangeRegex = /^\d+(\.\d+)?-\d+(\.\d+)?$/; + + if (rangeRegex.test(value)) { const [from, to] = value.split('-'); results.push({ attribute, @@ -263,7 +266,7 @@ function getFilterFromParams(filterParam) { }, }); } else { - // Handle single values (like categories with one value) + // Supports hyphens in category/url keys results.push({ attribute, in: [value], From 47dc38d951b139cef3ce0f42cd702f7e886f0f2c Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 12 Feb 2026 20:18:36 +0530 Subject: [PATCH 04/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index a57cf8f92c..a091df1631 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -252,7 +252,7 @@ function getFilterFromParams(filterParam) { attribute, in: value.split(commaRegex), }); - } + } const rangeRegex = /^\d+(\.\d+)?-\d+(\.\d+)?$/; From 59f37716dff7f838a42877c72cb5584a5355677d Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Wed, 18 Feb 2026 20:15:55 +0530 Subject: [PATCH 05/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index a091df1631..40153acb60 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -254,6 +254,7 @@ function getFilterFromParams(filterParam) { }); } + // Detect real numeric ranges only (e.g. 10-50, 19.99-29.99) const rangeRegex = /^\d+(\.\d+)?-\d+(\.\d+)?$/; if (rangeRegex.test(value)) { @@ -266,7 +267,7 @@ function getFilterFromParams(filterParam) { }, }); } else { - // Supports hyphens in category/url keys + // Supports hyphens in category/url keys, treat as a real string value instead of a range results.push({ attribute, in: [value], From 31ca5a0f13363c3ac81379a03fa540fba6af4720 Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 19:00:56 +0530 Subject: [PATCH 06/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index 40153acb60..f209a0850e 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -67,7 +67,7 @@ export default async function decorate(block) { pageSize: 8, sort: sort ? getSortFromParams(sort) : [{ attribute: 'position', direction: 'DESC' }], filter: [ - { attribute: 'categoryPath', eq: config.urlpath }, // Add category filter + { attribute: 'categories', in: [config.urlpath] }, // Add category filter { attribute: 'visibility', in: ['Search', 'Catalog, Search'] }, ...getFilterFromParams(filter), ], From b63ae558c826097e9cd1ba3d8eef816df43ec6ad Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 19:21:46 +0530 Subject: [PATCH 07/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index f209a0850e..e57faa49a1 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -61,15 +61,23 @@ export default async function decorate(block) { // Request search based on the page type on block load if (config.urlpath) { // If it's a category page... + const urlFilters = getFilterFromParams(filter); + // Detect if categories already exist in URL + const hasCategoryFilter = urlFilters.some((f) => f.attribute === 'categories'); + // Only inject base category if user hasn't selected any yet + const categoryFilter = hasCategoryFilter + ? [] + : [{ attribute: 'categories', in: [config.urlpath] }]; + await search({ - phrase: '', // search all products in the category + phrase: '', currentPage: page ? Number(page) : 1, pageSize: 8, sort: sort ? getSortFromParams(sort) : [{ attribute: 'position', direction: 'DESC' }], filter: [ - { attribute: 'categories', in: [config.urlpath] }, // Add category filter + ...categoryFilter, { attribute: 'visibility', in: ['Search', 'Catalog, Search'] }, - ...getFilterFromParams(filter), + ...urlFilters, ], }).catch(() => { console.error('Error searching for products'); From 1d699a705bc3b69218f14dc9f9274dabbb6d4cc7 Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 19:53:37 +0530 Subject: [PATCH 08/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index e57faa49a1..441ea994d1 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -243,45 +243,44 @@ function getParamsFromSort(sort) { function getFilterFromParams(filterParam) { if (!filterParam) return []; - // Decode the URL-encoded parameter const decodedParam = decodeURIComponent(filterParam); const results = []; const filters = decodedParam.split('|'); filters.forEach((filter) => { - if (filter.includes(':')) { - const [attribute, value] = filter.split(':'); - const commaRegex = /,(?!\s)/; - - if (commaRegex.test(value)) { - // Handle array values like categories, - // but allow for commas within an array value (eg. "Catalog, Search") - results.push({ - attribute, - in: value.split(commaRegex), - }); - } - - // Detect real numeric ranges only (e.g. 10-50, 19.99-29.99) - const rangeRegex = /^\d+(\.\d+)?-\d+(\.\d+)?$/; - - if (rangeRegex.test(value)) { - const [from, to] = value.split('-'); - results.push({ - attribute, - range: { - from: Number(from), - to: Number(to), - }, - }); - } else { - // Supports hyphens in category/url keys, treat as a real string value instead of a range - results.push({ - attribute, - in: [value], - }); - } + if (!filter.includes(':')) return; + + const [attribute, value] = filter.split(':'); + + // Detect numeric range (price etc.) + const rangeRegex = /^\d+(\.\d+)?-\d+(\.\d+)?$/; + if (rangeRegex.test(value)) { + const [from, to] = value.split('-'); + results.push({ + attribute, + range: { + from: Number(from), + to: Number(to), + }, + }); + return; + } + + // Detect multi-select (comma separated) + if (value.includes(',')) { + const values = [...new Set(value.split(','))]; + results.push({ + attribute, + in: values, + }); + return; } + + // Single value (supports hyphenated category keys) + results.push({ + attribute, + in: [value], + }); }); return results; From ff297a9644f90b575638b5c0d861ff0226f31f0f Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 20:17:01 +0530 Subject: [PATCH 09/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index 441ea994d1..3bbbf9755b 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -62,12 +62,17 @@ export default async function decorate(block) { if (config.urlpath) { // If it's a category page... const urlFilters = getFilterFromParams(filter); - // Detect if categories already exist in URL - const hasCategoryFilter = urlFilters.some((f) => f.attribute === 'categories'); - // Only inject base category if user hasn't selected any yet - const categoryFilter = hasCategoryFilter + + // Normalize urlpath (defensive — removes accidental slashes) + const baseCategory = config.urlpath?.replace(/^\/|\/$/g, ''); + + // Check whether URL already defines categories + const urlCategoryFilter = urlFilters.find((f) => f.attribute === 'categories'); + + // If no categories in URL → enforce base category + const categoryFilter = urlCategoryFilter ? [] - : [{ attribute: 'categories', in: [config.urlpath] }]; + : [{ attribute: 'categories', in: [baseCategory] }]; await search({ phrase: '', @@ -76,7 +81,7 @@ export default async function decorate(block) { sort: sort ? getSortFromParams(sort) : [{ attribute: 'position', direction: 'DESC' }], filter: [ ...categoryFilter, - { attribute: 'visibility', in: ['Search', 'Catalog, Search'] }, + { attribute: 'visibility', in: ['Search', 'Catalog', 'Catalog, Search'] }, ...urlFilters, ], }).catch(() => { From 7b99451307405c960d74508957de7aa45951ae3d Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 20:24:30 +0530 Subject: [PATCH 10/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index 3bbbf9755b..ab8e356e8f 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -95,7 +95,7 @@ export default async function decorate(block) { pageSize: 8, sort: getSortFromParams(sort), filter: [ - { attribute: 'visibility', in: ['Search', 'Catalog, Search'] }, + { attribute: 'visibility', in: ['Search', 'Catalog', 'Catalog, Search'] }, ...getFilterFromParams(filter), ], }).catch(() => { From e41a39d780f200e4b59b4730b5ab58058a0695df Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 20:37:27 +0530 Subject: [PATCH 11/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index ab8e356e8f..c8e0bcd70f 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -63,16 +63,21 @@ export default async function decorate(block) { // If it's a category page... const urlFilters = getFilterFromParams(filter); - // Normalize urlpath (defensive — removes accidental slashes) + // Normalize urlpath const baseCategory = config.urlpath?.replace(/^\/|\/$/g, ''); - // Check whether URL already defines categories - const urlCategoryFilter = urlFilters.find((f) => f.attribute === 'categories'); + // Detect filters already present + const hasCategory = urlFilters.some((f) => f.attribute === 'categories'); + const hasVisibility = urlFilters.some((f) => f.attribute === 'visibility'); - // If no categories in URL → enforce base category - const categoryFilter = urlCategoryFilter - ? [] - : [{ attribute: 'categories', in: [baseCategory] }]; + // Inject only when missing + const injectedFilters = [ + ...(hasCategory ? [] : [{ attribute: 'categories', in: [baseCategory] }]), + ...(hasVisibility ? [] : [{ + attribute: 'visibility', + in: ['Search', 'Catalog', 'Catalog, Search'], + }]), + ]; await search({ phrase: '', @@ -80,8 +85,7 @@ export default async function decorate(block) { pageSize: 8, sort: sort ? getSortFromParams(sort) : [{ attribute: 'position', direction: 'DESC' }], filter: [ - ...categoryFilter, - { attribute: 'visibility', in: ['Search', 'Catalog', 'Catalog, Search'] }, + ...injectedFilters, ...urlFilters, ], }).catch(() => { @@ -95,7 +99,7 @@ export default async function decorate(block) { pageSize: 8, sort: getSortFromParams(sort), filter: [ - { attribute: 'visibility', in: ['Search', 'Catalog', 'Catalog, Search'] }, + { attribute: 'visibility', in: ['Search', 'Catalog, Search'] }, ...getFilterFromParams(filter), ], }).catch(() => { From dd55a68529de341b99618c780630d0355ed32691 Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 21:06:54 +0530 Subject: [PATCH 12/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 69 ++++++++++++------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index c8e0bcd70f..4b13fdfaf0 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -61,33 +61,17 @@ export default async function decorate(block) { // Request search based on the page type on block load if (config.urlpath) { // If it's a category page... - const urlFilters = getFilterFromParams(filter); - - // Normalize urlpath + const parsedUrlFilters = getFilterFromParams(filter); const baseCategory = config.urlpath?.replace(/^\/|\/$/g, ''); - - // Detect filters already present - const hasCategory = urlFilters.some((f) => f.attribute === 'categories'); - const hasVisibility = urlFilters.some((f) => f.attribute === 'visibility'); - - // Inject only when missing - const injectedFilters = [ - ...(hasCategory ? [] : [{ attribute: 'categories', in: [baseCategory] }]), - ...(hasVisibility ? [] : [{ - attribute: 'visibility', - in: ['Search', 'Catalog', 'Catalog, Search'], - }]), - ]; - + + const finalFilters = normalizeFilters(parsedUrlFilters, baseCategory); + await search({ phrase: '', currentPage: page ? Number(page) : 1, pageSize: 8, sort: sort ? getSortFromParams(sort) : [{ attribute: 'position', direction: 'DESC' }], - filter: [ - ...injectedFilters, - ...urlFilters, - ], + filter: finalFilters, }).catch(() => { console.error('Error searching for products'); }); @@ -249,6 +233,43 @@ function getParamsFromSort(sort) { return sort.map((item) => `${item.attribute}_${item.direction}`).join(','); } +function normalizeFilters(urlFilters, baseCategory) { + const map = new Map(); + + // Always enforce base category unless categories already exist + if (!urlFilters.some((f) => f.attribute === 'categories')) { + map.set('categories', new Set([baseCategory])); + } + + urlFilters.forEach((filter) => { + // Ignore legacy categoryPath completely + if (filter.attribute === 'categoryPath') return; + + if (!map.has(filter.attribute)) { + map.set(filter.attribute, new Set()); + } + + if (filter.in) { + filter.in.forEach((val) => { + const clean = val.trim(); + if (clean) map.get(filter.attribute).add(clean); + }); + } + }); + + // Ensure visibility exists (required by Product Discovery) + if (!map.has('visibility')) { + map.set('visibility', new Set(['Search', 'Catalog', 'Catalog, Search'])); + } + + // Convert Map to API format + return Array.from(map.entries()).map(([attribute, values]) => ({ + attribute, + in: Array.from(values), + })); +} + + function getFilterFromParams(filterParam) { if (!filterParam) return []; @@ -277,13 +298,15 @@ function getFilterFromParams(filterParam) { // Detect multi-select (comma separated) if (value.includes(',')) { - const values = [...new Set(value.split(','))]; + const values = value.split(',').map((v) => v.trim()).filter(Boolean); + results.push({ attribute, - in: values, + in: [...new Set(values)], }); return; } + // Single value (supports hyphenated category keys) results.push({ From 092c2e7307c08ba188cd265a9605e13e5257dbc0 Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 21:12:13 +0530 Subject: [PATCH 13/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 70 +++++++------------ 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index 4b13fdfaf0..f49e2a2b0a 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -61,17 +61,33 @@ export default async function decorate(block) { // Request search based on the page type on block load if (config.urlpath) { // If it's a category page... - const parsedUrlFilters = getFilterFromParams(filter); + const urlFilters = getFilterFromParams(filter); + + // Normalize urlpath const baseCategory = config.urlpath?.replace(/^\/|\/$/g, ''); - - const finalFilters = normalizeFilters(parsedUrlFilters, baseCategory); - + + // Detect filters already present + const hasCategory = urlFilters.some((f) => f.attribute === 'categories'); + const hasVisibility = urlFilters.some((f) => f.attribute === 'visibility'); + + // Inject only when missing + const injectedFilters = [ + ...(hasCategory ? [] : [{ attribute: 'categories', in: [baseCategory] }]), + ...(hasVisibility ? [] : [{ + attribute: 'visibility', + in: ['Search', 'Catalog', 'Catalog, Search'], + }]), + ]; + await search({ phrase: '', currentPage: page ? Number(page) : 1, pageSize: 8, sort: sort ? getSortFromParams(sort) : [{ attribute: 'position', direction: 'DESC' }], - filter: finalFilters, + filter: [ + ...injectedFilters, + ...urlFilters, + ], }).catch(() => { console.error('Error searching for products'); }); @@ -233,43 +249,6 @@ function getParamsFromSort(sort) { return sort.map((item) => `${item.attribute}_${item.direction}`).join(','); } -function normalizeFilters(urlFilters, baseCategory) { - const map = new Map(); - - // Always enforce base category unless categories already exist - if (!urlFilters.some((f) => f.attribute === 'categories')) { - map.set('categories', new Set([baseCategory])); - } - - urlFilters.forEach((filter) => { - // Ignore legacy categoryPath completely - if (filter.attribute === 'categoryPath') return; - - if (!map.has(filter.attribute)) { - map.set(filter.attribute, new Set()); - } - - if (filter.in) { - filter.in.forEach((val) => { - const clean = val.trim(); - if (clean) map.get(filter.attribute).add(clean); - }); - } - }); - - // Ensure visibility exists (required by Product Discovery) - if (!map.has('visibility')) { - map.set('visibility', new Set(['Search', 'Catalog', 'Catalog, Search'])); - } - - // Convert Map to API format - return Array.from(map.entries()).map(([attribute, values]) => ({ - attribute, - in: Array.from(values), - })); -} - - function getFilterFromParams(filterParam) { if (!filterParam) return []; @@ -298,8 +277,11 @@ function getFilterFromParams(filterParam) { // Detect multi-select (comma separated) if (value.includes(',')) { - const values = value.split(',').map((v) => v.trim()).filter(Boolean); - + const values = value + .split(',') + .map((v) => v.trim()) + .filter(Boolean); + results.push({ attribute, in: [...new Set(values)], From 5f6ab82331c3f4383799be15eb08383aff1b9a70 Mon Sep 17 00:00:00 2001 From: Kaushik Pitroda Date: Thu, 19 Feb 2026 21:14:56 +0530 Subject: [PATCH 14/14] USF-3715: Allowed categories with hyphens --- blocks/product-list-page/product-list-page.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/blocks/product-list-page/product-list-page.js b/blocks/product-list-page/product-list-page.js index f49e2a2b0a..b69fe7403e 100644 --- a/blocks/product-list-page/product-list-page.js +++ b/blocks/product-list-page/product-list-page.js @@ -276,6 +276,15 @@ function getFilterFromParams(filterParam) { } // Detect multi-select (comma separated) + if (value.includes(',')) { + const values = [...new Set(value.split(','))]; + results.push({ + attribute, + in: values, + }); + return; + } + if (value.includes(',')) { const values = value .split(',')