@scayle/storefront-product-detail 0.2.0 → 0.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.
@@ -1,4 +1,4 @@
1
- import { type ComputedRef } from 'vue';
1
+ import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
2
2
  import type { BreadcrumbItem, Variant } from '@scayle/storefront-nuxt';
3
3
  import type { WithContext, Product, BreadcrumbList } from 'schema-dts';
4
4
  type CanonicalLink = Record<'rel' | 'key' | 'href', string>;
@@ -10,7 +10,7 @@ interface ProductInfo {
10
10
  variants: Variant[];
11
11
  }
12
12
  export interface UseProductSeoDataReturn {
13
- title: string;
13
+ title: ComputedRef<string>;
14
14
  robots: string;
15
15
  canonicalLink: ComputedRef<CanonicalLink[]>;
16
16
  productJsonLd: ComputedRef<WithContext<Product>>;
@@ -19,10 +19,10 @@ export interface UseProductSeoDataReturn {
19
19
  /**
20
20
  * Composable function to generate SEO-related data for a product page.
21
21
  *
22
- * @param {BreadcrumbItem[]} breadcrumbs - Array of breadcrumb items representing the navigation path to the product.
23
- * @param {UrlParams} urlParams - Object containing baseUrl and fullPath to build and sanitize canonicalUrl.
24
- * @param {string[]} images - Array of image URLs for the product.
25
- * @param {ProductInfo} productInfo - Object containing product information including name, brand, description, and variants.
22
+ * @param {MaybeRefOrGetter<BreadcrumbItem[]>} breadcrumbs - Array of breadcrumb items representing the navigation path to the product.
23
+ * @param {MaybeRefOrGetter<UrlParams>} urlParams - Object containing baseUrl and fullPath to build and sanitize canonicalUrl.
24
+ * @param {MaybeRefOrGetter<string[]>} images - Array of image URLs for the product.
25
+ * @param {MaybeRefOrGetter<ProductInfo>} productInfo - Object containing product information including name, brand, description, and variants.
26
26
  * @property {string} name - The name of the product.
27
27
  * @property {string} brand - The brand of the product.
28
28
  * @property {string} productDescription - A description of the product.
@@ -31,9 +31,9 @@ export interface UseProductSeoDataReturn {
31
31
  * @returns {UseProductSeoDataReturn} An object containing SEO data for the product page with following properties:
32
32
  * @property {string} robots - Robots meta tag value, typically "index, follow".
33
33
  * @property {ComputedRef<CanonicalLink[]>} canonicalLink - Computed ref for canonical link tag metadata.
34
- * @property {string} title - The title of the product.
34
+ * @property {ComputedRef<string>} title - The title of the product.
35
35
  * @property {ComputedRef<WithContext<Product>>} productJsonLd - Computed ref containing JSON-LD structured data for the product.
36
36
  * @property {ComputedRef<WithContext<BreadcrumbList>>} productBreadcrumbJsonLd - Computed ref containing JSON-LD structured data for product breadcrumbs.
37
37
  */
38
- export declare function useProductSeoData(breadcrumbs: BreadcrumbItem[], urlParams: UrlParams, images: string[], productInfo: ProductInfo): UseProductSeoDataReturn;
38
+ export declare function useProductSeoData(breadcrumbs: MaybeRefOrGetter<BreadcrumbItem[]>, urlParams: MaybeRefOrGetter<UrlParams>, images: MaybeRefOrGetter<string[]>, productInfo: MaybeRefOrGetter<ProductInfo>): UseProductSeoDataReturn;
39
39
  export {};
@@ -1,4 +1,4 @@
1
- import { computed } from "vue";
1
+ import { computed, toValue } from "vue";
2
2
  import {
3
3
  generateCategoryBreadcrumbSchema,
4
4
  sanitizeCanonicalURL
@@ -6,7 +6,7 @@ import {
6
6
  import { generateProductSchema } from "../utils/seo.js";
7
7
  export function useProductSeoData(breadcrumbs, urlParams, images, productInfo) {
8
8
  const canonicalUrl = computed(() => {
9
- const url = `${urlParams.baseUrl}${urlParams.fullPath}`;
9
+ const url = `${toValue(urlParams).baseUrl}${toValue(urlParams).fullPath}`;
10
10
  return sanitizeCanonicalURL(url, []);
11
11
  });
12
12
  const canonicalLink = computed(() => {
@@ -15,21 +15,21 @@ export function useProductSeoData(breadcrumbs, urlParams, images, productInfo) {
15
15
  const robots = "index, follow";
16
16
  const productJsonLd = computed(() => {
17
17
  return generateProductSchema({
18
- productName: productInfo.name || "",
19
- brandName: productInfo.brand || "",
18
+ productName: toValue(productInfo).name || "",
19
+ brandName: toValue(productInfo).brand || "",
20
20
  url: canonicalUrl.value,
21
- variants: productInfo.variants || [],
22
- images,
23
- description: productInfo.productDescription
21
+ variants: toValue(productInfo).variants || [],
22
+ images: toValue(images),
23
+ description: toValue(productInfo).productDescription
24
24
  });
25
25
  });
26
26
  const productBreadcrumbJsonLd = computed(() => {
27
- return generateCategoryBreadcrumbSchema(breadcrumbs);
27
+ return generateCategoryBreadcrumbSchema(toValue(breadcrumbs));
28
28
  });
29
29
  return {
30
30
  robots,
31
31
  canonicalLink,
32
- title: productInfo.name,
32
+ title: computed(() => toValue(productInfo).name),
33
33
  productJsonLd,
34
34
  productBreadcrumbJsonLd
35
35
  };
@@ -1,6 +1,7 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { useProductSeoData } from "~/src/runtime";
3
3
  import { variantFactory } from "@scayle/storefront-nuxt/dist/test/factories";
4
+ import { ref } from "vue";
4
5
  describe("useProductSeoData", () => {
5
6
  it("should return product seo data from product", () => {
6
7
  const {
@@ -20,7 +21,7 @@ describe("useProductSeoData", () => {
20
21
  productDescription: "description"
21
22
  }
22
23
  );
23
- expect(title).toBe("name");
24
+ expect(title.value).toBe("name");
24
25
  expect(productBreadcrumbJsonLd.value).toStrictEqual({
25
26
  "@context": "https://schema.org",
26
27
  "@type": "BreadcrumbList",
@@ -65,4 +66,114 @@ describe("useProductSeoData", () => {
65
66
  }
66
67
  ]);
67
68
  });
69
+ it("should handle reactivity correctly with changing parameters", () => {
70
+ const breadcrubs = [{ value: "value", to: "/" }];
71
+ const urlParams = { baseUrl: "baseUrl", fullPath: "fullPath" };
72
+ const productInfo = ref({
73
+ name: "name",
74
+ variants: [variantFactory.build({ id: 1 })],
75
+ brand: "brand",
76
+ productDescription: "description"
77
+ });
78
+ const {
79
+ title,
80
+ productBreadcrumbJsonLd,
81
+ productJsonLd,
82
+ canonicalLink
83
+ } = useProductSeoData(
84
+ breadcrubs,
85
+ urlParams,
86
+ [],
87
+ productInfo
88
+ );
89
+ expect(title.value).toBe("name");
90
+ expect(productBreadcrumbJsonLd.value).toStrictEqual({
91
+ "@context": "https://schema.org",
92
+ "@type": "BreadcrumbList",
93
+ "itemListElement": [
94
+ {
95
+ "@type": "ListItem",
96
+ "item": "/",
97
+ "name": "value",
98
+ "position": 1
99
+ }
100
+ ]
101
+ });
102
+ expect(productJsonLd.value).toStrictEqual({
103
+ "@context": "https://schema.org",
104
+ "@type": "Product",
105
+ "brand": {
106
+ "@type": "Brand",
107
+ "name": "brand"
108
+ },
109
+ "description": "description",
110
+ "image": [],
111
+ "name": "name",
112
+ "offers": [
113
+ {
114
+ "@type": "Offer",
115
+ "availability": "https://schema.org/InStock",
116
+ "itemCondition": "https://schema.org/NewCondition",
117
+ "mpn": void 0,
118
+ "price": 1,
119
+ "priceCurrency": "USD",
120
+ "sku": "1"
121
+ }
122
+ ],
123
+ "url": "baseUrlfullPath"
124
+ });
125
+ expect(canonicalLink.value).toStrictEqual([
126
+ {
127
+ "href": "baseUrlfullPath",
128
+ "key": "canonical",
129
+ "rel": "canonical"
130
+ }
131
+ ]);
132
+ breadcrubs[0].to = "/other";
133
+ urlParams.baseUrl = "otherUrl";
134
+ productInfo.value.name = "otherName";
135
+ expect(title.value).toBe("otherName");
136
+ expect(productBreadcrumbJsonLd.value).toStrictEqual({
137
+ "@context": "https://schema.org",
138
+ "@type": "BreadcrumbList",
139
+ "itemListElement": [
140
+ {
141
+ "@type": "ListItem",
142
+ "item": "/other",
143
+ "name": "value",
144
+ "position": 1
145
+ }
146
+ ]
147
+ });
148
+ expect(productJsonLd.value).toStrictEqual({
149
+ "@context": "https://schema.org",
150
+ "@type": "Product",
151
+ "brand": {
152
+ "@type": "Brand",
153
+ "name": "brand"
154
+ },
155
+ "description": "description",
156
+ "image": [],
157
+ "name": "otherName",
158
+ "offers": [
159
+ {
160
+ "@type": "Offer",
161
+ "availability": "https://schema.org/InStock",
162
+ "itemCondition": "https://schema.org/NewCondition",
163
+ "mpn": void 0,
164
+ "price": 1,
165
+ "priceCurrency": "USD",
166
+ "sku": "1"
167
+ }
168
+ ],
169
+ "url": "otherUrlfullPath"
170
+ });
171
+ expect(canonicalLink.value).toStrictEqual([
172
+ {
173
+ "href": "otherUrlfullPath",
174
+ "key": "canonical",
175
+ "rel": "canonical"
176
+ }
177
+ ]);
178
+ });
68
179
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-product-detail",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Collection of essential composables and utilities to work with product detail",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -41,10 +41,10 @@
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@nuxt/kit": "^3.13.0",
44
- "@scayle/storefront-nuxt": "^7.93.0",
44
+ "@scayle/storefront-nuxt": "^7.93.0 || ^8.0.0",
45
45
  "@vue/test-utils": "2.4.6",
46
- "@vueuse/core": "11.2.0",
47
- "vue-router": "4.4.5"
46
+ "@vueuse/core": "12.0.0",
47
+ "vue-router": "4.5.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@nuxt/kit": "3.13.2",
@@ -52,19 +52,19 @@
52
52
  "@nuxt/schema": "3.13.2",
53
53
  "@nuxt/test-utils": "3.14.4",
54
54
  "@scayle/eslint-config-storefront": "4.3.2",
55
- "@scayle/storefront-nuxt": "7.95.0",
56
- "@types/node": "22.9.0",
55
+ "@scayle/storefront-nuxt": "8.0.0",
56
+ "@types/node": "22.10.1",
57
57
  "@vue/test-utils": "2.4.6",
58
- "@vueuse/core": "11.2.0",
59
- "dprint": "0.47.5",
60
- "eslint": "9.14.0",
61
- "happy-dom": "15.11.6",
58
+ "@vueuse/core": "12.0.0",
59
+ "dprint": "0.47.6",
60
+ "eslint": "9.16.0",
61
+ "happy-dom": "15.11.7",
62
62
  "eslint-formatter-gitlab": "5.1.0",
63
63
  "nuxt": "3.13.2",
64
64
  "publint": "0.2.12",
65
65
  "typescript": "5.6.3",
66
- "vitest": "2.1.5",
67
- "vue-router": "4.4.5",
66
+ "vitest": "2.1.8",
67
+ "vue-router": "4.5.0",
68
68
  "vue-tsc": "2.1.10"
69
69
  }
70
70
  }