@scayle/storefront-product-listing 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,165 @@
1
+ # @scayle/storefront-product-listing
2
+
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add `buildQueryFromCategoryFilters` utility.
8
+
9
+ ## 1.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Update `APISortOrder` and `APISortOption` names due to update to newest `@scayle/storefront-nuxt` version.
14
+
15
+ ## 1.1.3
16
+
17
+ ### Patch Changes
18
+
19
+ - Ensure the module meta has the correct package version
20
+
21
+ ## 1.1.2
22
+
23
+ ### Patch Changes
24
+
25
+ - Update `useFiltersForListing` to automatically refresh filter data whenever the its parameter change. This will ensure `filteredProductCount` is up to date.
26
+
27
+ ## 1.1.1
28
+
29
+ ### Patch Changes
30
+
31
+ - We've updated to `nuxt@3.14`
32
+
33
+ ## 1.1.0
34
+
35
+ ### Minor Changes
36
+
37
+ - Updates the parameter type of `useFiltersForListing` from `MaybeRefOrGetter` to `Ref` to enforce that the parameter is a mandatory reactive reference.
38
+ Non-reactive parameter will not work as expected in the Boilerplate.
39
+ - Updates the parameter type of `useProductsForListing` from `MaybeRefOrGetter` to `Ref` to enforce that the parameter is a mandatory reactive reference.
40
+ Non-reactive parameter will not work as expected in the Boilerplate.
41
+
42
+ ## 1.0.0
43
+
44
+ ### Major Changes
45
+
46
+ - This release introduces the `@scayle/storefront-product-listing` package, decoupling composables and utilities of the Product Listing Page (PLP) functionality from the SCAYLE Storefront Boilerplate for enhanced modularity and integration.
47
+ This separation empowers developers with greater control over PLP updates and simplifies its integration into existing Storefront-based projects.
48
+
49
+ - [Discover more about the Product Listing Page for SCAYLE Storefront in our SCAYLE Resource Center.](https://scayle.dev/en/storefront-guide/developer-guide/features/pages/product-listing-page)
50
+
51
+ - This release requires `@scayle/storefront-nuxt@8.x` or higher. Support for `@scayle/storefront-nuxt@7.x` has been discontinued. Please update your dependencies accordingly. The `peerDependency` range has been updated to `^8.0.0`.
52
+
53
+ ### Minor Changes
54
+
55
+ - Compared to the original implementation within the SCAYLE Storefront Boilerplate, the `@scayle/storefront-product-detail` package has received some improvements and refactoring:
56
+
57
+ - The `useAppliedFilters` composable has been added to the `storefront-product-listing` package.
58
+ It now accepts a `filtersPrefix` parameter, allowing you to specify a custom prefix for filter-related query parameters.
59
+ - The `useProductListSort` composable has been added to the `storefront-product-listing` package.
60
+ It now accepts a `sortingOptions` and `defaultSortingKey` parameter, to config the sorting options and set the initial sorting value.
61
+ - The `useProductListFilter` composable has been added to the `storefront-product-listing` package.
62
+ It accepts `currentCategoryId`, which represents the active category, and `ProductListFilterOptions` for specifying options for the underlying `useFilters` composable.
63
+ - The `filters` utility has been added to the `storefront-product-listing` package.
64
+ It provides utilities for filter query manipulation.
65
+ - All exports are now defined in a single file, and everything is exposed via the `#storefront-product-listing` import.
66
+ - The `useProductsByCategory` composable has been added to the `storefront-product-listing` package.
67
+ It now accepts a `categoryId` and `ProductsByCategoryOptions` object that has
68
+ certain fetching products by category params(`category` & `categoryId`), `fetchingOptions`, `productsPerPage`,
69
+ `fetchProductsCacheTtl`, `cacheKeyPrefix`, `fetchingKey`, `withParams` (for product)
70
+ and `query` (current route query state).
71
+ - The `useProductListingSeoData` composable has been added to the `storefront-product-listing` package.
72
+ It accepts reactive `category`, `breadcrumbs`, an object of type `UrlParams` (containing a `baseUrl` and `fullPath`) which are used for building SEO data.
73
+ - Product Listing-related test factories are now exposed externally.
74
+ They can be used as follows:
75
+
76
+ ```ts
77
+ import { filtersFactory } from '@scayle/storefront-product-listing'
78
+ ```
79
+
80
+ - Upgrade the `storefront-core` and `storefront-nuxt` packages to version 8 and update your implementation to reflect the changes in RPC composables:
81
+ - The composables `useProductListFilter` and `useProductsByCategory` now return a `status` property instead of `fetching`. The `status` can have one of the following values: `idle`, `pending`, `error`, or `success`.
82
+ - The `fetch` method has been replaced by `refresh`. Update your logic accordingly to use the new method.
83
+
84
+ ## 0.6.0
85
+
86
+ ### Minor Changes
87
+
88
+ - Renamed the composables `useProductListFilter` to `useFiltersForListing`.
89
+ - Composable `useProductListFilter` now allows configuration of the parameters `includeSellableForFree`, `includeSoldOut`, `orFiltersOperator`.
90
+ - Removes the internal call to `useAppliedFilters` and `useProductListSort` which need to be explicitly passed to `useProductsForListing` and `useFiltersForListing`
91
+ - Renamed the composables `useProductsByCategory` to `useProductsForListing`.
92
+ - The configurable caching options `cacheKeyPrefix` and `fetchProductsCacheTtl` have been removed from the `useProductsByCategory` composable.
93
+ - Removed unnecessary parameter `category` and return value `activeCategoryName` from `useProductListingSeoData`.
94
+ - The `productsPerPage` parameter in the `useProductsByCategory` composable is now required and no longer provides a default value.
95
+ - Removed the `getClearedPriceQuery` utility function. Price filter resetting can now be achieved via the `getClearedFilterQueryByKey` function.
96
+
97
+ ## 0.5.0
98
+
99
+ ### Minor Changes
100
+
101
+ - Allow Storefront Nuxt v8 as a peer dependency
102
+
103
+ ## 0.4.2
104
+
105
+ ### Patch Changes
106
+
107
+ - The `useProductsByCategory` composable now sets the trackSearchAnalyticsEvent parameter by default if the current page evaluates to `1` and the `appliedFilter.term` is not empty.
108
+ The default behavior can be overriden by passing `trackSearchAnalyticsEvent: false` as part of `ProductsByCategoryOptions.params`.
109
+ For more details, check the [SCAYLE Search Analytics section](https://scayle.dev/en/developer-guide/products/search#scayle-search-analytics) in the SCAYLE Resource Center.
110
+
111
+ ## 0.4.1
112
+
113
+ ### Patch Changes
114
+
115
+ - Resolved an issue where the default parameter values for `useProductsByCategory` like `productsPerPage` or `cacheKeyPrefix` were not being set correctly.
116
+
117
+ ## 0.4.0
118
+
119
+ ### Minor Changes
120
+
121
+ - Fix computed `sortLinks` return type mismatch in `UseProductListSortReturn` interface.
122
+
123
+ ## 0.3.0
124
+
125
+ ### Minor Changes
126
+
127
+ - Fix `sortLinks` type by extending the `SelectedSort` type with `to` property which represents the `RouteLocationRaw` type from `vue-router`.
128
+
129
+ ## 0.2.0
130
+
131
+ ### Minor Changes
132
+
133
+ - Add and expose externally explicit return types/interfaces for all product listing composables.
134
+
135
+ ## 0.1.4
136
+
137
+ ### Patch Changes
138
+
139
+ - Resolved reactivity issues in composables by ensuring they receive the full `route` object instead of just the `query` object.
140
+ This allows for more reliable updates when the route changes.
141
+
142
+ ## 0.1.3
143
+
144
+ ### Patch Changes
145
+
146
+ - Resolve an issue where `sortLinks` in `useProductListSort` caused broken URLs on the PLP.
147
+ The `sortLinks` now include a `path` attribute reflecting the current route path.
148
+
149
+ ## 0.1.2
150
+
151
+ ### Patch Changes
152
+
153
+ - Update import of test factories to use new entry point
154
+
155
+ ## 0.1.1
156
+
157
+ ### Patch Changes
158
+
159
+ - Resolved issue where sorting did not correctly route to the first page.
160
+
161
+ ## 0.1.0
162
+
163
+ ### Minor Changes
164
+
165
+ - Introduce `@scayle/storefront-product-listing` package
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 SCAYLE GmbH
3
+ Copyright (c) 2025 SCAYLE GmbH
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/module.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@scayle/storefront-product-listing",
3
3
  "configKey": "product-listing",
4
- "version": "1.1.3",
4
+ "version": "1.3.0",
5
5
  "compatibility": {
6
6
  "bridge": false,
7
7
  "nuxt": ">=3.13"
8
8
  },
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "0.8.4",
11
- "unbuild": "3.2.0"
11
+ "unbuild": "3.5.0"
12
12
  }
13
13
  }
package/dist/module.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineNuxtModule, createResolver, addImportsDir } from '@nuxt/kit';
2
2
 
3
3
  const PACKAGE_NAME = "@scayle/storefront-product-listing";
4
- const PACKAGE_VERSION = "1.1.3";
4
+ const PACKAGE_VERSION = "1.3.0";
5
5
  const module = defineNuxtModule({
6
6
  meta: {
7
7
  name: PACKAGE_NAME,
@@ -0,0 +1,5 @@
1
+ export * from './useAppliedFilters.js';
2
+ export * from './useFiltersForListing.js';
3
+ export * from './useProductListSort.js';
4
+ export * from './useProductListingSeoData.js';
5
+ export * from './useProductsForListing.js';
@@ -0,0 +1,5 @@
1
+ export * from "./useAppliedFilters.js";
2
+ export * from "./useFiltersForListing.js";
3
+ export * from "./useProductListSort.js";
4
+ export * from "./useProductListingSeoData.js";
5
+ export * from "./useProductsForListing.js";
@@ -1,4 +1,4 @@
1
- import { type ComputedRef } from 'vue';
1
+ import type { ComputedRef } from 'vue';
2
2
  import type { ProductSearchQuery } from '@scayle/storefront-nuxt';
3
3
  import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
4
4
  export type AppliedAttributeValues = Record<string, (string | number)[]>;
@@ -1,6 +1,6 @@
1
- import { type FetchFiltersParams } from '@scayle/storefront-nuxt';
1
+ import type { FetchFiltersParams } from '@scayle/storefront-nuxt';
2
2
  import { useFilters } from '@scayle/storefront-nuxt/composables';
3
- import { type ComputedRef, type Ref } from 'vue';
3
+ import type { ComputedRef, Ref } from 'vue';
4
4
  import type { FilterItemWithValues } from '@scayle/storefront-api';
5
5
  export type FiltersForListingOptions = Partial<{
6
6
  params: Ref<Omit<FetchFiltersParams, 'category'>>;
@@ -1,5 +1,5 @@
1
- import { type ProductSortConfig } from '@scayle/storefront-nuxt';
2
- import { type ComputedRef } from 'vue';
1
+ import type { ProductSortConfig } from '@scayle/storefront-nuxt';
2
+ import type { ComputedRef } from 'vue';
3
3
  import type { RouteLocationNormalizedLoadedGeneric, RouteLocationRaw } from 'vue-router';
4
4
  type SelectedSort = ProductSortConfig & {
5
5
  key: string;
@@ -1,38 +1,35 @@
1
- import {
2
- APISortOption,
3
- APISortOrder
4
- } from "@scayle/storefront-nuxt";
1
+ import { APISortOption, APISortOrder } from "@scayle/storefront-nuxt";
5
2
  import { computed } from "vue";
6
3
  export function useProductListSort(route, { sortingOptions, defaultSortingKey } = {
7
4
  sortingOptions: [
8
5
  {
9
6
  key: "top_seller",
10
7
  sortingKey: "scayle:v1:recommended",
11
- direction: APISortOrder.Descending,
8
+ direction: APISortOrder.DESCENDING,
12
9
  label: "sorting_select.top_seller"
13
10
  },
14
11
  {
15
12
  key: "date_newest",
16
- by: APISortOption.DateAdded,
17
- direction: APISortOrder.Descending,
13
+ by: APISortOption.DATE_ADDED,
14
+ direction: APISortOrder.DESCENDING,
18
15
  label: "sorting_select.date_newest"
19
16
  },
20
17
  {
21
18
  key: "price_desc",
22
- by: APISortOption.Price,
23
- direction: APISortOrder.Descending,
19
+ by: APISortOption.PRICE,
20
+ direction: APISortOrder.DESCENDING,
24
21
  label: "sorting_select.price_desc"
25
22
  },
26
23
  {
27
24
  key: "price_asc",
28
- by: APISortOption.Price,
29
- direction: APISortOrder.Ascending,
25
+ by: APISortOption.PRICE,
26
+ direction: APISortOrder.ASCENDING,
30
27
  label: "sorting_select.price_asc"
31
28
  },
32
29
  {
33
30
  key: "reduction_desc",
34
- by: APISortOption.Reduction,
35
- direction: APISortOrder.Descending,
31
+ by: APISortOption.REDUCTION,
32
+ direction: APISortOrder.DESCENDING,
36
33
  label: "sorting_select.reduction_desc"
37
34
  }
38
35
  ],
@@ -1,4 +1,4 @@
1
- import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
1
+ import type { ComputedRef, MaybeRefOrGetter } from 'vue';
2
2
  import type { BreadcrumbItem } from '@scayle/storefront-nuxt';
3
3
  import type { BreadcrumbList, WithContext } from 'schema-dts';
4
4
  import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
@@ -1,5 +1,5 @@
1
- import { type FetchProductsByCategoryParams, type Product } from '@scayle/storefront-nuxt';
2
- import { type Ref, type ComputedRef } from 'vue';
1
+ import type { FetchProductsByCategoryParams, Product } from '@scayle/storefront-nuxt';
2
+ import type { Ref, ComputedRef } from 'vue';
3
3
  import { useProducts } from '@scayle/storefront-nuxt/composables';
4
4
  import type { Pagination } from '@scayle/storefront-api';
5
5
  export type UseProductsForListingReturn = Pick<Awaited<ReturnType<typeof useProducts>>, 'error' | 'status'> & {
@@ -1,6 +1,4 @@
1
- import {
2
- extendPromise
3
- } from "@scayle/storefront-nuxt";
1
+ import { extendPromise } from "@scayle/storefront-nuxt";
4
2
  import { computed, ref } from "vue";
5
3
  import { useProducts } from "@scayle/storefront-nuxt/composables";
6
4
  export function useProductsForListing({
@@ -1,5 +1,5 @@
1
1
  import type { LocationQuery, RouteLocationNormalizedLoadedGeneric } from 'vue-router';
2
- import type { ProductSearchQuery } from '@scayle/storefront-api';
2
+ import type { CategoryFilter, ProductSearchQuery } from '@scayle/storefront-nuxt';
3
3
  import type { RangeTuple } from '~/src';
4
4
  /**
5
5
  * Parses filter data from a Vue Router route object.
@@ -59,3 +59,11 @@ export declare const createNewBoolAttributeQuery: (route: RouteLocationNormalize
59
59
  * @returns A new query object with the filter cleared.
60
60
  */
61
61
  export declare const getClearedFilterQueryByKey: (route: RouteLocationNormalizedLoadedGeneric, key: string) => LocationQuery;
62
+ /**
63
+ * Builds a new query object from category filters.
64
+ *
65
+ * @param filters - Category filters.
66
+ *
67
+ * @returns A new query object from Category filters.
68
+ */
69
+ export declare const buildQueryFromCategoryFilters: (filters: CategoryFilter[]) => LocationQuery;
@@ -1,5 +1,5 @@
1
1
  const getAttributeValues = (value) => {
2
- return value.split(",").filter((v) => v !== "").map((v) => Number(v)).filter((v) => !isNaN(v));
2
+ return value.split(",").filter((v) => v !== "").map(Number).filter((v) => !isNaN(v));
3
3
  };
4
4
  export const parseFilterDataFromRoute = (route, filtersPrefix) => {
5
5
  const queryKeys = Object.keys(route.query);
@@ -9,7 +9,10 @@ export const parseFilterDataFromRoute = (route, filtersPrefix) => {
9
9
  if (!queryKey.includes(filtersPrefix)) {
10
10
  return;
11
11
  }
12
- const filterName = queryKey.replace(filtersPrefix, "").replace("]", "").replace("[", "");
12
+ const filterName = queryKey.replace(filtersPrefix, "").replace(
13
+ /[\[\]]/g,
14
+ ""
15
+ );
13
16
  switch (filterName) {
14
17
  case "minPrice":
15
18
  case "maxPrice":
@@ -122,3 +125,20 @@ export const getClearedFilterQueryByKey = (route, key) => {
122
125
  }
123
126
  return query;
124
127
  };
128
+ export const buildQueryFromCategoryFilters = (filters) => {
129
+ return filters.reduce((query, filter) => {
130
+ switch (filter.type) {
131
+ case "attribute":
132
+ return {
133
+ ...query,
134
+ [`filters[${filter.attributeFilter.group.key}]`]: filter.attributeFilter.values.map(({ id }) => id).join(",")
135
+ };
136
+ case "boolean":
137
+ return {
138
+ ...query,
139
+ [`filters[${filter.booleanFilter.slug}]`]: "true"
140
+ };
141
+ }
142
+ return query;
143
+ }, {});
144
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-product-listing",
3
- "version": "1.1.3",
3
+ "version": "1.3.0",
4
4
  "description": "Collection of essential composables and utilities to work with product listing",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -18,11 +18,16 @@
18
18
  "types": "./dist/module.d.ts",
19
19
  "require": "./dist/module.cjs",
20
20
  "import": "./dist/module.mjs"
21
+ },
22
+ "./composables": {
23
+ "types": "./dist/runtime/composables/index.d.ts",
24
+ "import": "./dist/runtime/composables/index.js"
21
25
  }
22
26
  },
23
27
  "main": "./dist/index.mjs",
24
28
  "types": "./dist/index.d.ts",
25
29
  "files": [
30
+ "CHANGELOG.md",
26
31
  "dist"
27
32
  ],
28
33
  "imports": {
@@ -39,7 +44,7 @@
39
44
  "format:fix": "dprint fmt",
40
45
  "test": "vitest --run --passWithNoTests",
41
46
  "test:watch": "vitest --passWithNoTests",
42
- "test:ci": "vitest --run --passWithNoTests --coverage --reporter=default --reporter=junit --outputFile=./junit.xml",
47
+ "test:ci": "vitest --run --passWithNoTests --coverage --reporter=default --reporter=junit --outputFile=./coverage/junit.xml",
43
48
  "typecheck": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
44
49
  "package:lint": "publint"
45
50
  },
@@ -47,31 +52,31 @@
47
52
  "@nuxt/kit": "^3.13.0",
48
53
  "@scayle/storefront-nuxt": "^8.0.0",
49
54
  "@vue/test-utils": "2.4.6",
50
- "@vueuse/core": "12.3.0",
51
- "fishery": "2.2.2",
55
+ "@vueuse/core": "12.7.0",
56
+ "fishery": "2.2.3",
52
57
  "vue-router": "4.5.0"
53
58
  },
54
59
  "devDependencies": {
55
- "@nuxt/kit": "3.14.1592",
60
+ "@nuxt/kit": "3.15.4",
56
61
  "@nuxt/module-builder": "0.8.4",
57
- "@nuxt/schema": "3.14.1592",
58
- "@nuxt/test-utils": "3.15.1",
59
- "@scayle/eslint-config-storefront": "4.4.0",
60
- "@scayle/storefront-nuxt": "8.3.2",
61
- "@types/node": "22.10.5",
62
+ "@nuxt/schema": "3.15.4",
63
+ "@nuxt/test-utils": "3.17.1",
64
+ "@scayle/eslint-config-storefront": "4.4.1",
65
+ "@scayle/storefront-nuxt": "8.11.6",
66
+ "@types/node": "22.13.9",
62
67
  "@vue/test-utils": "2.4.6",
63
- "@vueuse/core": "12.3.0",
64
- "dprint": "0.48.0",
65
- "eslint": "9.17.0",
66
- "happy-dom": "16.5.3",
68
+ "@vueuse/core": "12.7.0",
69
+ "dprint": "0.49.0",
70
+ "eslint": "9.21.0",
71
+ "happy-dom": "17.1.9",
67
72
  "eslint-formatter-gitlab": "5.1.0",
68
- "nuxt": "3.14.1592",
73
+ "nuxt": "3.15.4",
69
74
  "publint": "0.2.12",
70
- "typescript": "5.7.3",
71
- "vitest": "2.1.8",
72
- "schema-dts": "1.1.2",
75
+ "typescript": "5.8.2",
76
+ "vitest": "2.1.9",
77
+ "schema-dts": "1.1.5",
73
78
  "vue-router": "4.5.0",
74
- "fishery": "2.2.2",
75
- "vue-tsc": "2.2.0"
79
+ "fishery": "2.2.3",
80
+ "vue-tsc": "2.2.8"
76
81
  }
77
82
  }