Skip to content

Commit 8231321

Browse files
yurynixttsahi
authored andcommitted
Align API of loadProductsListSearchServiceConfig and loadProductsListServiceConfig (#118)
* Align API of loadProductsListSearchServiceConfig and loadProductsListServiceConfig * Align the API more * Bump stores version
1 parent ab03ffa commit 8231321

File tree

5 files changed

+151
-41
lines changed

5 files changed

+151
-41
lines changed

examples/astro-stores-demo/src/pages/category/[categorySlug].astro

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import {
77
loadCategoryServiceConfig,
88
loadProductsListSearchServiceConfig,
99
loadProductsListServiceConfig,
10-
parseUrlForProductsListSearch,
10+
parseUrlToSearchOptions,
1111
} from '@wix/headless-stores/services';
1212
import BaseLayout from '../../layouts/BaseLayout.astro';
1313
import StoreCollectionPage from '../../react-pages/category/index';
1414
1515
// Get category slug from URL params
1616
const { categorySlug = '' } = Astro.params;
1717
18-
// Load categories first so we can pass them to parseUrlForProductsListSearch
18+
// Load categories first so we can pass them to parseUrlToSearchOptions
1919
const categoriesListConfig = await loadCategoriesListServiceConfig();
2020
const category = categoriesListConfig.categories.find(
2121
category => category.slug === categorySlug
@@ -27,7 +27,8 @@ if (!category) {
2727
2828
const categoryName = category?.name || '';
2929
30-
const { searchOptions } = await parseUrlForProductsListSearch(
30+
// Parse URL to get search options with defaults for this category
31+
const parsed = await parseUrlToSearchOptions(
3132
Astro.url.href,
3233
categoriesListConfig.categories,
3334
{
@@ -48,9 +49,9 @@ const [
4849
productsListSearchConfig,
4950
seoTagsServiceConfig,
5051
] = await Promise.all([
51-
loadProductsListServiceConfig(searchOptions),
52+
loadProductsListServiceConfig(parsed), // Both services now use same parsed result
5253
loadCurrentCartServiceConfig(),
53-
loadProductsListSearchServiceConfig(Astro.url.href),
54+
loadProductsListSearchServiceConfig(parsed),
5455
loadSEOTagsServiceConfig({
5556
pageUrl: Astro.url.href,
5657
itemData: {

packages/headless-components/stores/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@wix/headless-stores",
3-
"version": "0.0.48",
3+
"version": "0.0.49",
44
"type": "module",
55
"scripts": {
66
"prebuild": "cd ../media && yarn build && cd ../ecom && yarn build",

packages/headless-components/stores/src/services/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ export {
4545
ProductsListSearchServiceDefinition,
4646
ProductsListSearchServiceConfig,
4747
loadProductsListSearchServiceConfig,
48-
parseUrlForProductsListSearch,
48+
parseUrlToSearchOptions,
4949
convertUrlSortToSortType,
5050
} from "./products-list-search-service.js";

packages/headless-components/stores/src/services/products-list-search-service.ts

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export interface ProductChoice {
4949
/**
5050
* Initial search state that can be loaded from URL parameters.
5151
*/
52-
type InitialSearchState = {
52+
export type InitialSearchState = {
5353
sort?: SortType;
5454
limit?: number;
5555
cursor?: string | null;
@@ -293,9 +293,33 @@ function updateUrlWithSearchState(searchState: {
293293
}
294294

295295
/**
296-
* Parse URL and build complete search options with all filters, sort, and pagination
296+
* Parse URL and build complete search options with all filters, sort, and pagination.
297+
* This function extracts search parameters, filters, sorting, and pagination from a URL
298+
* and converts them into the format expected by the Wix Stores API.
299+
*
300+
* @param {string} url - The URL to parse search parameters from
301+
* @param {Category[]} categoriesList - List of available categories for category slug resolution
302+
* @param {productsV3.V3ProductSearch} [defaultSearchOptions] - Default search options to merge with parsed URL parameters
303+
* @returns {Promise<{searchOptions: productsV3.V3ProductSearch, initialSearchState: InitialSearchState}>}
304+
* Object containing both API-ready search options and UI-ready initial state
305+
*
306+
* @example
307+
* ```tsx
308+
* // Parse URL with filters, sort, and pagination
309+
* const categories = await loadCategoriesListServiceConfig();
310+
* const { searchOptions, initialSearchState } = await parseUrlToSearchOptions(
311+
* 'https://example.com/products?sort=price:desc&Color=red,blue&minPrice=50',
312+
* categories.categories
313+
* );
314+
*
315+
* // Use searchOptions for API calls
316+
* const products = await productsV3.searchProducts(searchOptions);
317+
*
318+
* // Use initialSearchState for UI initialization
319+
* const filterState = initialSearchState.productOptions; // { colorId: ['red-id', 'blue-id'] }
320+
* ```
297321
*/
298-
export async function parseUrlForProductsListSearch(
322+
export async function parseUrlToSearchOptions(
299323
url: string,
300324
categoriesList: Category[],
301325
defaultSearchOptions?: productsV3.V3ProductSearch,
@@ -570,18 +594,57 @@ export async function parseUrlForProductsListSearch(
570594
}
571595

572596
/**
573-
* Load search service configuration from URL
597+
* Load search service configuration from URL or parsed URL result.
598+
* This function provides the configuration for the Products List Search service,
599+
* including customizations and initial search state.
600+
*
601+
* @param {string | { searchOptions: productsV3.V3ProductSearch; initialSearchState: InitialSearchState }} input - Either a URL to parse or parsed URL result from parseUrlToSearchOptions
602+
* @returns {Promise<ProductsListSearchServiceConfig>} Promise that resolves to the search service configuration
603+
*
604+
* @example
605+
* ```tsx
606+
* // Option 1: Load from URL (will parse filters, sort, pagination from URL params)
607+
* const searchConfig = await loadProductsListSearchServiceConfig(window.location.href);
608+
*
609+
* // Option 2: Custom parsing with defaults
610+
* const categories = await loadCategoriesListServiceConfig();
611+
* const parsed = await parseUrlToSearchOptions(
612+
* window.location.href,
613+
* categories.categories,
614+
* {
615+
* cursorPaging: { limit: 12 },
616+
* filter: { 'categoryIds': ['123'] },
617+
* sort: [{ fieldName: 'name' as const, order: 'ASC' as const }]
618+
* }
619+
* );
620+
* const searchConfig = await loadProductsListSearchServiceConfig(parsed);
621+
*
622+
* // Option 3: Performance optimization - use parsed result for both services (no duplicate parsing)
623+
* const categories = await loadCategoriesListServiceConfig();
624+
* const parsed = await parseUrlToSearchOptions(url, categories.categories);
625+
* const [productsConfig, searchConfig] = await Promise.all([
626+
* loadProductsListServiceConfig(parsed),
627+
* loadProductsListSearchServiceConfig(parsed),
628+
* ]);
629+
* ```
574630
*/
575631
export async function loadProductsListSearchServiceConfig(
576-
url: string,
632+
input: string | { searchOptions: productsV3.V3ProductSearch; initialSearchState: InitialSearchState },
577633
): Promise<ProductsListSearchServiceConfig> {
578-
// Load categories using the categories service
579-
const categoriesListConfig = await loadCategoriesListServiceConfig();
580-
581-
const { initialSearchState } = await parseUrlForProductsListSearch(
582-
url,
583-
categoriesListConfig.categories,
584-
);
634+
let initialSearchState: InitialSearchState;
635+
636+
if (typeof input === 'string') {
637+
// URL input - parse it
638+
const categoriesListConfig = await loadCategoriesListServiceConfig();
639+
const { initialSearchState: parsedState } = await parseUrlToSearchOptions(
640+
input,
641+
categoriesListConfig.categories,
642+
);
643+
initialSearchState = parsedState;
644+
} else {
645+
// Parsed URL result - use initialSearchState directly (no duplicate work)
646+
initialSearchState = input.initialSearchState;
647+
}
585648

586649
const { items: customizations = [] } = await customizationsV3
587650
.queryCustomizations()

packages/headless-components/stores/src/services/products-list-service.ts

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
type Signal,
55
} from "@wix/services-definitions/core-services/signals";
66
import { productsV3, readOnlyVariantsV3 } from "@wix/stores";
7+
import { loadCategoriesListServiceConfig } from "./categories-list-service.js";
8+
import { parseUrlToSearchOptions, type InitialSearchState } from "./products-list-search-service.js";
79

810
export const DEFAULT_QUERY_LIMIT = 100;
911

@@ -27,27 +29,33 @@ export type ProductsListServiceConfig = {
2729
/**
2830
* Loads products list service configuration from the Wix Stores API for SSR initialization.
2931
* This function is designed to be used during Server-Side Rendering (SSR) to preload
30-
* a list of products based on search criteria.
32+
* a list of products based on search criteria or URL parameters.
3133
*
32-
* @param {productsV3.V3ProductSearch} searchOptions - The search options for querying products
34+
* @param {string | { searchOptions: productsV3.V3ProductSearch; initialSearchState: InitialSearchState }} input - Either a URL to parse or parsed URL result from parseUrlToSearchOptions
3335
* @returns {Promise<ProductsListServiceConfig>} Promise that resolves to the products list configuration
3436
*
3537
* @example
3638
* ```astro
3739
* ---
3840
* // Astro page example - pages/products.astro
39-
* import { loadProductsListServiceConfig } from '@wix/stores/services';
41+
* import { loadProductsListServiceConfig, parseUrlToSearchOptions, loadCategoriesListServiceConfig } from '@wix/stores/services';
4042
* import { ProductList } from '@wix/stores/components';
4143
*
42-
* // Define search options
43-
* const searchOptions = {
44-
* cursorPaging: { limit: 12 },
45-
* filter: {},
46-
* sort: [{ fieldName: 'name', order: 'ASC' }]
47-
* };
44+
* // Option 1: Load from URL (will parse filters, sort, pagination from URL params)
45+
* const productsConfig = await loadProductsListServiceConfig(Astro.url.href);
4846
*
49-
* // Load products data during SSR
50-
* const productsConfig = await loadProductsListServiceConfig(searchOptions);
47+
* // Option 2: Custom parsing with defaults
48+
* const categories = await loadCategoriesListServiceConfig();
49+
* const parsed = await parseUrlToSearchOptions(
50+
* Astro.url.href,
51+
* categories.categories,
52+
* {
53+
* cursorPaging: { limit: 12 },
54+
* filter: {},
55+
* sort: [{ fieldName: 'name' as const, order: 'ASC' as const }]
56+
* }
57+
* );
58+
* const productsConfig = await loadProductsListServiceConfig(parsed);
5159
* ---
5260
*
5361
* <ProductList.Root productsConfig={productsConfig}>
@@ -66,23 +74,31 @@ export type ProductsListServiceConfig = {
6674
* ```tsx
6775
* // Next.js page example - pages/products.tsx
6876
* import { GetServerSideProps } from 'next';
69-
* import { loadProductsListServiceConfig } from '@wix/stores/services';
77+
* import { loadProductsListServiceConfig, parseUrlToSearchOptions, loadCategoriesListServiceConfig } from '@wix/stores/services';
7078
* import { ProductsList } from '@wix/stores/components';
7179
*
7280
* interface ProductsPageProps {
7381
* productsConfig: Awaited<ReturnType<typeof loadProductsListServiceConfig>>;
7482
* }
7583
*
76-
* export const getServerSideProps: GetServerSideProps<ProductsPageProps> = async () => {
77-
* const searchOptions = {
78-
* cursorPaging: { limit: 12 },
79-
* filter: {
80-
* 'allCategoriesInfo.categories': { $matchItems: [{ _id: { $in: [category._id] } }] }
81-
* },
82-
* sort: [{ fieldName: 'name' as const, order: 'ASC' as const }]
83-
* };
84+
* export const getServerSideProps: GetServerSideProps<ProductsPageProps> = async ({ req }) => {
85+
* // Option 1: Parse from URL
86+
* const productsConfig = await loadProductsListServiceConfig(`${req.url}`);
8487
*
85-
* const productsConfig = await loadProductsListServiceConfig(searchOptions);
88+
* // Option 2: Custom parsing with filters
89+
* const categories = await loadCategoriesListServiceConfig();
90+
* const parsed = await parseUrlToSearchOptions(
91+
* `${req.url}`,
92+
* categories.categories,
93+
* {
94+
* cursorPaging: { limit: 12 },
95+
* filter: {
96+
* 'allCategoriesInfo.categories': { $matchItems: [{ _id: { $in: [category._id] } }] }
97+
* },
98+
* sort: [{ fieldName: 'name' as const, order: 'ASC' as const }]
99+
* }
100+
* );
101+
* const productsConfig = await loadProductsListServiceConfig(parsed);
86102
*
87103
* return {
88104
* props: {
@@ -106,10 +122,40 @@ export type ProductsListServiceConfig = {
106122
* );
107123
* }
108124
* ```
125+
*
126+
* @example
127+
* ```tsx
128+
* // Advanced: Performance optimization when using both services
129+
* import { parseUrlToSearchOptions, loadProductsListServiceConfig, loadProductsListSearchServiceConfig, loadCategoriesListServiceConfig } from '@wix/stores/services';
130+
*
131+
* const categories = await loadCategoriesListServiceConfig();
132+
* const parsed = await parseUrlToSearchOptions(url, categories.categories);
133+
*
134+
* // Both services use the same parsed result (no duplicate URL parsing)
135+
* const [productsConfig, searchConfig] = await Promise.all([
136+
* loadProductsListServiceConfig(parsed),
137+
* loadProductsListSearchServiceConfig(parsed)
138+
* ]);
139+
* ```
109140
*/
110141
export async function loadProductsListServiceConfig(
111-
searchOptions: productsV3.V3ProductSearch,
142+
input: string | { searchOptions: productsV3.V3ProductSearch; initialSearchState: InitialSearchState },
112143
): Promise<ProductsListServiceConfig> {
144+
let searchOptions: productsV3.V3ProductSearch;
145+
146+
if (typeof input === 'string') {
147+
// URL input - parse it
148+
const categoriesListConfig = await loadCategoriesListServiceConfig();
149+
const { searchOptions: parsedOptions } = await parseUrlToSearchOptions(
150+
input,
151+
categoriesListConfig.categories
152+
);
153+
searchOptions = parsedOptions;
154+
} else {
155+
// Parsed URL result - use searchOptions directly
156+
searchOptions = input.searchOptions;
157+
}
158+
113159
const searchWithoutFilter = { ...searchOptions, filter: {} };
114160

115161
const [resultWithoutFilter, resultWithFilter] = await Promise.all([

0 commit comments

Comments
 (0)