@shopware/helpers 1.5.0 → 1.6.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/README.md CHANGED
@@ -36,8 +36,25 @@ export default defineConfig({
36
36
 
37
37
  Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/helpers/CHANGELOG.md)
38
38
 
39
- ### Latest changes: 1.5.0
39
+ ### Latest changes: 1.6.0
40
40
 
41
41
  ### Minor Changes
42
42
 
43
- - [#1910](https://github.com/shopware/frontends/pull/1910) [`c8fa438`](https://github.com/shopware/frontends/commit/c8fa438b38de6dbc43a2895f2d1906907447c384) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Add helper for getting payment and shipping method icon
43
+ - [#1985](https://github.com/shopware/frontends/pull/1985) [`2cbda25`](https://github.com/shopware/frontends/commit/2cbda257a1056454e12f2fba9052f83eecb6d986) Thanks [@mkucmus](https://github.com/mkucmus)! - Added methods to extract product info:
44
+ - `isProductOnSale`
45
+ - `isProductTopSeller`
46
+ - `getProductManufacturerName`
47
+
48
+ - [#2176](https://github.com/shopware/frontends/pull/2176) [`c647baf`](https://github.com/shopware/frontends/commit/c647baf93e7174b849f5961ee5803add99d78602) Thanks [@mkucmus](https://github.com/mkucmus)! - - Add `getProductListingFromCmsPage` helper to extract product listing from CMS page structure
49
+ - Enable early access to listing data during SSR before component tree renders
50
+
51
+ ### Patch Changes
52
+
53
+ - [#2030](https://github.com/shopware/frontends/pull/2030) [`22ff62e`](https://github.com/shopware/frontends/commit/22ff62e354f024599d64ea8096af57695248851c) Thanks [@mkucmus](https://github.com/mkucmus)! - Extended `ListingFilter` type to support property options and manufacturer entities:
54
+ - Added optional `options` property for property group options
55
+ - Added optional `entities` property for manufacturer entities
56
+ - Improved type safety by explicitly typing empty array return in `getListingFilters()`
57
+
58
+ - [#2153](https://github.com/shopware/frontends/pull/2153) [`56cd178`](https://github.com/shopware/frontends/commit/56cd178e25fe2399b7170ccac3044e980621f041) Thanks [@mkucmus](https://github.com/mkucmus)! - Fix `getSrcSetForMedia` to properly encode special characters (spaces, commas, parentheses) in media URLs
59
+
60
+ - [#2162](https://github.com/shopware/frontends/pull/2162) [`e1fae3e`](https://github.com/shopware/frontends/commit/e1fae3eb6430e5c8e133456fbaf7f215f80c36f6) Thanks [@mkucmus](https://github.com/mkucmus)! - Export encodeUrlPath helper function to properly handle special characters (spaces, commas) in image URLs
package/dist/index.d.mts CHANGED
@@ -22,7 +22,7 @@ declare function getProductFreeShipping<T extends {
22
22
  * @category Product
23
23
  */
24
24
  declare function getMainImageUrl<T extends {
25
- cover?: {
25
+ cover: {
26
26
  media?: {
27
27
  url: string;
28
28
  };
@@ -34,9 +34,11 @@ declare function getMainImageUrl<T extends {
34
34
  };
35
35
  }>;
36
36
  } | {
37
- cover?: {
37
+ cover: {
38
38
  url: string;
39
39
  };
40
+ } | {
41
+ cover: null;
40
42
  }>(object: T): string;
41
43
 
42
44
  /**
@@ -205,6 +207,18 @@ declare function getProductFromPrice<T extends {
205
207
  calculatedPrices?: CalculatedPrice[];
206
208
  }>(product: T): number | undefined;
207
209
 
210
+ interface ProductWithManufacturer {
211
+ manufacturer?: {
212
+ translated?: {
213
+ name?: string;
214
+ };
215
+ };
216
+ }
217
+ /**
218
+ * Gets the translated name of the product manufacturer
219
+ */
220
+ declare function getProductManufacturerName(product: ProductWithManufacturer): string;
221
+
208
222
  /**
209
223
  * Get product route information for Vue router.
210
224
  *
@@ -228,6 +242,24 @@ declare function getProductRoute<T extends {
228
242
  };
229
243
  };
230
244
 
245
+ /**
246
+ * Checks if a product is on sale based on its price percentage
247
+ */
248
+ declare function isProductOnSale(product: {
249
+ calculatedPrice: {
250
+ listPrice?: {
251
+ percentage?: number;
252
+ } | null;
253
+ };
254
+ }): boolean;
255
+
256
+ /**
257
+ * Checks if a product is marked as a top seller
258
+ */
259
+ declare function isProductTopSeller(product: {
260
+ markAsTopseller?: boolean;
261
+ }): boolean;
262
+
231
263
  /**
232
264
  * Get URL for category.
233
265
  * Some link {@link isLinkCategory}
@@ -418,6 +450,35 @@ declare function getCmsBreadcrumbs<T extends {
418
450
  name: string;
419
451
  }[];
420
452
 
453
+ type CmsPageStructure = {
454
+ sections: Array<{
455
+ blocks?: Array<{
456
+ slots?: Array<{
457
+ type?: string;
458
+ data?: unknown;
459
+ [key: string]: unknown;
460
+ }>;
461
+ [key: string]: unknown;
462
+ }>;
463
+ [key: string]: unknown;
464
+ }>;
465
+ [key: string]: unknown;
466
+ };
467
+ /**
468
+ * Extracts the product listing data from a CMS page structure.
469
+ * Useful for SSR to get listing data early before components render.
470
+ *
471
+ * @param cmsPage - The CMS page object (Schemas["CmsPage"])
472
+ * @returns The product listing result or null if not found
473
+ *
474
+ * @example
475
+ * ```ts
476
+ * const listing = getProductListingFromCmsPage(cmsPage);
477
+ * createCategoryListingContext(listing);
478
+ * ```
479
+ */
480
+ declare function getProductListingFromCmsPage<T = unknown>(cmsPage: CmsPageStructure): T | null;
481
+
421
482
  type Category = {
422
483
  apiAlias: "category";
423
484
  };
@@ -475,6 +536,18 @@ type ListingFilter = {
475
536
  code: string;
476
537
  id: string;
477
538
  name: string;
539
+ options?: Array<{
540
+ id: string;
541
+ translated?: {
542
+ name?: string;
543
+ };
544
+ }>;
545
+ entities?: Array<{
546
+ id: string;
547
+ translated?: {
548
+ name?: string;
549
+ };
550
+ }>;
478
551
  };
479
552
  /**
480
553
  * @beta
@@ -557,6 +630,16 @@ declare function getSmallestThumbnailUrl<T extends {
557
630
  }>;
558
631
  }>(media?: T): string | undefined;
559
632
 
633
+ /**
634
+ * Encodes URL pathname to handle special characters (spaces, commas, etc.)
635
+ *
636
+ * @param urlString - The URL string to encode
637
+ * @returns The URL with encoded pathname
638
+ *
639
+ * @public
640
+ * @category Media
641
+ */
642
+ declare function encodeUrlPath(urlString: string): string;
560
643
  /**
561
644
  * Returns the srcset attribute for the image, for available breakpoints
562
645
  *
@@ -632,4 +715,4 @@ declare function canUseQuoteActions<T extends {
632
715
  };
633
716
  }>(quote: T): boolean;
634
717
 
635
- export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, buildUrlPrefix, canUseQuoteActions, downloadFile, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, relativeUrlSlash, urlIsAbsolute };
718
+ export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, type UrlRouteOutput, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ declare function getProductFreeShipping<T extends {
22
22
  * @category Product
23
23
  */
24
24
  declare function getMainImageUrl<T extends {
25
- cover?: {
25
+ cover: {
26
26
  media?: {
27
27
  url: string;
28
28
  };
@@ -34,9 +34,11 @@ declare function getMainImageUrl<T extends {
34
34
  };
35
35
  }>;
36
36
  } | {
37
- cover?: {
37
+ cover: {
38
38
  url: string;
39
39
  };
40
+ } | {
41
+ cover: null;
40
42
  }>(object: T): string;
41
43
 
42
44
  /**
@@ -205,6 +207,18 @@ declare function getProductFromPrice<T extends {
205
207
  calculatedPrices?: CalculatedPrice[];
206
208
  }>(product: T): number | undefined;
207
209
 
210
+ interface ProductWithManufacturer {
211
+ manufacturer?: {
212
+ translated?: {
213
+ name?: string;
214
+ };
215
+ };
216
+ }
217
+ /**
218
+ * Gets the translated name of the product manufacturer
219
+ */
220
+ declare function getProductManufacturerName(product: ProductWithManufacturer): string;
221
+
208
222
  /**
209
223
  * Get product route information for Vue router.
210
224
  *
@@ -228,6 +242,24 @@ declare function getProductRoute<T extends {
228
242
  };
229
243
  };
230
244
 
245
+ /**
246
+ * Checks if a product is on sale based on its price percentage
247
+ */
248
+ declare function isProductOnSale(product: {
249
+ calculatedPrice: {
250
+ listPrice?: {
251
+ percentage?: number;
252
+ } | null;
253
+ };
254
+ }): boolean;
255
+
256
+ /**
257
+ * Checks if a product is marked as a top seller
258
+ */
259
+ declare function isProductTopSeller(product: {
260
+ markAsTopseller?: boolean;
261
+ }): boolean;
262
+
231
263
  /**
232
264
  * Get URL for category.
233
265
  * Some link {@link isLinkCategory}
@@ -418,6 +450,35 @@ declare function getCmsBreadcrumbs<T extends {
418
450
  name: string;
419
451
  }[];
420
452
 
453
+ type CmsPageStructure = {
454
+ sections: Array<{
455
+ blocks?: Array<{
456
+ slots?: Array<{
457
+ type?: string;
458
+ data?: unknown;
459
+ [key: string]: unknown;
460
+ }>;
461
+ [key: string]: unknown;
462
+ }>;
463
+ [key: string]: unknown;
464
+ }>;
465
+ [key: string]: unknown;
466
+ };
467
+ /**
468
+ * Extracts the product listing data from a CMS page structure.
469
+ * Useful for SSR to get listing data early before components render.
470
+ *
471
+ * @param cmsPage - The CMS page object (Schemas["CmsPage"])
472
+ * @returns The product listing result or null if not found
473
+ *
474
+ * @example
475
+ * ```ts
476
+ * const listing = getProductListingFromCmsPage(cmsPage);
477
+ * createCategoryListingContext(listing);
478
+ * ```
479
+ */
480
+ declare function getProductListingFromCmsPage<T = unknown>(cmsPage: CmsPageStructure): T | null;
481
+
421
482
  type Category = {
422
483
  apiAlias: "category";
423
484
  };
@@ -475,6 +536,18 @@ type ListingFilter = {
475
536
  code: string;
476
537
  id: string;
477
538
  name: string;
539
+ options?: Array<{
540
+ id: string;
541
+ translated?: {
542
+ name?: string;
543
+ };
544
+ }>;
545
+ entities?: Array<{
546
+ id: string;
547
+ translated?: {
548
+ name?: string;
549
+ };
550
+ }>;
478
551
  };
479
552
  /**
480
553
  * @beta
@@ -557,6 +630,16 @@ declare function getSmallestThumbnailUrl<T extends {
557
630
  }>;
558
631
  }>(media?: T): string | undefined;
559
632
 
633
+ /**
634
+ * Encodes URL pathname to handle special characters (spaces, commas, etc.)
635
+ *
636
+ * @param urlString - The URL string to encode
637
+ * @returns The URL with encoded pathname
638
+ *
639
+ * @public
640
+ * @category Media
641
+ */
642
+ declare function encodeUrlPath(urlString: string): string;
560
643
  /**
561
644
  * Returns the srcset attribute for the image, for available breakpoints
562
645
  *
@@ -632,4 +715,4 @@ declare function canUseQuoteActions<T extends {
632
715
  };
633
716
  }>(quote: T): boolean;
634
717
 
635
- export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, buildUrlPrefix, canUseQuoteActions, downloadFile, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, relativeUrlSlash, urlIsAbsolute };
718
+ export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, type UrlRouteOutput, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
package/dist/index.mjs CHANGED
@@ -91,6 +91,10 @@ function getProductFromPrice(product) {
91
91
  return void 0;
92
92
  }
93
93
 
94
+ function getProductManufacturerName(product) {
95
+ return product.manufacturer?.translated?.name || "";
96
+ }
97
+
94
98
  function getProductRoute(product) {
95
99
  return {
96
100
  path: getProductUrl(product),
@@ -101,6 +105,14 @@ function getProductRoute(product) {
101
105
  };
102
106
  }
103
107
 
108
+ function isProductOnSale(product) {
109
+ return (product.calculatedPrice?.listPrice?.percentage ?? 0) > 0;
110
+ }
111
+
112
+ function isProductTopSeller(product) {
113
+ return !!product.markAsTopseller;
114
+ }
115
+
104
116
  function getEntityPrefix(category) {
105
117
  switch (category.linkType) {
106
118
  case "category":
@@ -308,6 +320,12 @@ function getCmsBreadcrumbs(page) {
308
320
  ];
309
321
  }
310
322
 
323
+ function getProductListingFromCmsPage(cmsPage) {
324
+ const slot = cmsPage.sections.flatMap((section) => section.blocks ?? []).flatMap((block) => block.slots ?? []).find((slot2) => slot2.type === "product-listing");
325
+ const listing = slot?.data?.listing;
326
+ return listing ?? null;
327
+ }
328
+
311
329
  function getCmsEntityObject(response) {
312
330
  switch (response.resourceType) {
313
331
  case "frontend.detail.page":
@@ -418,12 +436,21 @@ function getSmallestThumbnailUrl(media) {
418
436
  return lowest?.url;
419
437
  }
420
438
 
439
+ function encodeUrlPath(urlString) {
440
+ try {
441
+ const url = new URL(urlString);
442
+ url.pathname = url.pathname.split("/").map((segment) => encodeURIComponent(decodeURIComponent(segment))).join("/");
443
+ return url.toString();
444
+ } catch {
445
+ return urlString;
446
+ }
447
+ }
421
448
  function getSrcSetForMedia(media) {
422
449
  if (!media?.thumbnails?.length) {
423
450
  return;
424
451
  }
425
452
  return media.thumbnails.map((thumbnail) => {
426
- return thumbnail.url ? `${thumbnail.url} ${thumbnail.width}w` : void 0;
453
+ return thumbnail.url ? `${encodeUrlPath(thumbnail.url)} ${thumbnail.width}w` : void 0;
427
454
  }).filter((value) => !!value).join(", ");
428
455
  }
429
456
 
@@ -455,4 +482,4 @@ function canUseQuoteActions(quote) {
455
482
  return ["replied"].includes(quote.stateMachineState?.technicalName ?? "");
456
483
  }
457
484
 
458
- export { buildUrlPrefix, canUseQuoteActions, downloadFile, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, relativeUrlSlash, urlIsAbsolute };
485
+ export { buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopware/helpers",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Shopware helpers for accessing API data",
5
5
  "author": "Shopware",
6
6
  "type": "module",
@@ -33,10 +33,10 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@biomejs/biome": "1.8.3",
36
- "@vitest/coverage-v8": "3.2.3",
37
- "happy-dom": "18.0.1",
36
+ "@vitest/coverage-v8": "3.2.4",
37
+ "happy-dom": "20.0.10",
38
38
  "unbuild": "2.0.0",
39
- "vitest": "3.2.3",
39
+ "vitest": "3.2.4",
40
40
  "tsconfig": "0.0.0"
41
41
  },
42
42
  "scripts": {