@fast-simon/shopify-hydrogen 1.0.7-beta.0 → 1.0.8-beta.1

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.
@@ -327,6 +327,8 @@ export interface ShowHideFiltersButtonSettings {
327
327
  border: string;
328
328
  borderRadius: string;
329
329
  text: string;
330
+ textShow?: string;
331
+ textHide?: string;
330
332
  color: string;
331
333
  dimensions: DimensionsSettings;
332
334
  backgroundColor: string;
@@ -0,0 +1,90 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ vi.mock('@shopify/hydrogen', () => ({
3
+ CacheShort: () => ({ cacheStrategy: 'short' }),
4
+ fetchSync: (url) => {
5
+ lastRequestedUrl = url;
6
+ return { json: () => ({ facets: [], facets_completed: true, items: [] }) };
7
+ },
8
+ }));
9
+ let lastRequestedUrl = '';
10
+ import { CategoriesNavigation } from '../categories_navigation';
11
+ const baseProps = {
12
+ categoryID: '42',
13
+ storeID: 1,
14
+ UUID: 'abc',
15
+ };
16
+ describe('legacy hydrogen categories_navigation — personalization', () => {
17
+ beforeEach(() => {
18
+ lastRequestedUrl = '';
19
+ });
20
+ it('omits personalization keys when the prop is absent (backwards-compat)', () => {
21
+ CategoriesNavigation.get({ ...baseProps });
22
+ expect(lastRequestedUrl).not.toMatch(/[?&]ps=/);
23
+ expect(lastRequestedUrl).not.toMatch(/[?&]st=/);
24
+ expect(lastRequestedUrl).not.toMatch(/[?&]sp=/);
25
+ expect(lastRequestedUrl).not.toMatch(/[?&]spv=/);
26
+ });
27
+ it('emits full personalization context', () => {
28
+ CategoriesNavigation.get({
29
+ ...baseProps,
30
+ personalization: { st: 'tok', sp: '1,2', spv: 'vec', ps: 'new' },
31
+ });
32
+ expect(lastRequestedUrl).toMatch(/[?&]ps=new(&|$)/);
33
+ expect(lastRequestedUrl).toMatch(/[?&]st=tok(&|$)/);
34
+ expect(lastRequestedUrl).toMatch(/[?&]sp=1,2(&|$)/);
35
+ expect(lastRequestedUrl).toMatch(/[?&]spv=vec(&|$)/);
36
+ });
37
+ it('emits only provided partial context keys', () => {
38
+ CategoriesNavigation.get({
39
+ ...baseProps,
40
+ personalization: { ps: 'returning' },
41
+ });
42
+ expect(lastRequestedUrl).toMatch(/[?&]ps=returning(&|$)/);
43
+ expect(lastRequestedUrl).not.toMatch(/[?&]st=/);
44
+ expect(lastRequestedUrl).not.toMatch(/[?&]sp=/);
45
+ expect(lastRequestedUrl).not.toMatch(/[?&]spv=/);
46
+ });
47
+ it('empty personalization object emits no personalization keys', () => {
48
+ CategoriesNavigation.get({
49
+ ...baseProps,
50
+ personalization: {},
51
+ });
52
+ expect(lastRequestedUrl).not.toMatch(/[?&]ps=/);
53
+ });
54
+ it('getFacetsOnly emits personalization keys too', () => {
55
+ CategoriesNavigation.getFacetsOnly({
56
+ ...baseProps,
57
+ personalization: { ps: 'returning' },
58
+ });
59
+ expect(lastRequestedUrl).toMatch(/[?&]ps=returning(&|$)/);
60
+ expect(lastRequestedUrl).toMatch(/\/categories_navigation\?/);
61
+ });
62
+ });
63
+ describe('legacy hydrogen categories_navigation — regression (byte-identical without opt-in)', () => {
64
+ beforeEach(() => {
65
+ lastRequestedUrl = '';
66
+ });
67
+ it('URL is unchanged when personalization is omitted vs undefined', () => {
68
+ CategoriesNavigation.get({ ...baseProps });
69
+ const urlA = lastRequestedUrl;
70
+ lastRequestedUrl = '';
71
+ CategoriesNavigation.get({ ...baseProps, personalization: undefined });
72
+ expect(lastRequestedUrl).toBe(urlA);
73
+ });
74
+ it('preserves the historical narrow=undefined quirk when narrow is not provided', () => {
75
+ CategoriesNavigation.get({ ...baseProps });
76
+ expect(lastRequestedUrl).toMatch(/[?&]narrow=undefined/);
77
+ });
78
+ it('appends personalization keys after core params', () => {
79
+ CategoriesNavigation.get({
80
+ ...baseProps,
81
+ personalization: { ps: 'new' },
82
+ });
83
+ const params = new URL(lastRequestedUrl).searchParams;
84
+ const keys = [...params.keys()];
85
+ const idxCategory = keys.indexOf('category_id');
86
+ const idxPs = keys.indexOf('ps');
87
+ expect(idxCategory).toBeGreaterThanOrEqual(0);
88
+ expect(idxPs).toBeGreaterThan(idxCategory);
89
+ });
90
+ });
@@ -25,6 +25,22 @@ export interface CategoryNavigationServerResponseAll {
25
25
  personalization_recency_limit?: number;
26
26
  personalized_products?: number[];
27
27
  }
28
+ /**
29
+ * Optional personalization context for category navigation requests. When provided, the
30
+ * present keys are forwarded to the API. When omitted, the request URL is byte-identical
31
+ * to prior versions of this SDK.
32
+ *
33
+ * - `st` — session token
34
+ * - `sp` — recent-products (comma-joined IDs)
35
+ * - `spv` — personalization vector
36
+ * - `ps` — personalization segment (`"new"` | `"returning"`)
37
+ */
38
+ export interface PersonalizationContext {
39
+ st?: string;
40
+ sp?: string;
41
+ spv?: string;
42
+ ps?: 'new' | 'returning';
43
+ }
28
44
  interface CategoryNavigationProps {
29
45
  categoryID: string;
30
46
  page?: number;
@@ -36,13 +52,18 @@ interface CategoryNavigationProps {
36
52
  searchWithinResultsQuery?: string;
37
53
  storeID: number;
38
54
  UUID: string;
55
+ /**
56
+ * Optional personalization context for this request. When omitted, the emitted URL is
57
+ * unchanged from earlier SDK versions.
58
+ */
59
+ personalization?: PersonalizationContext;
39
60
  }
40
61
  interface CategoryNavigationResponse {
41
62
  data: CategoryNavigationServerResponseAll;
42
63
  getFacetsOnly?: () => ServerFacet[];
43
64
  }
44
65
  export declare const CategoriesNavigation: {
45
- get: ({ categoryID, page, sortBy, facets, narrow, productsPerPage, withProductAttributes, searchWithinResultsQuery, storeID, UUID, }: CategoryNavigationProps) => CategoryNavigationResponse;
46
- getFacetsOnly: ({ categoryID, page, sortBy, narrow, productsPerPage, searchWithinResultsQuery, storeID, UUID }: CategoryNavigationProps) => ServerFacet[];
66
+ get: ({ categoryID, page, sortBy, facets, narrow, productsPerPage, withProductAttributes, searchWithinResultsQuery, storeID, UUID, personalization, }: CategoryNavigationProps) => CategoryNavigationResponse;
67
+ getFacetsOnly: ({ categoryID, page, sortBy, narrow, productsPerPage, searchWithinResultsQuery, storeID, UUID, personalization, }: CategoryNavigationProps) => ServerFacet[];
47
68
  };
48
69
  export {};
@@ -2,7 +2,7 @@ import { CacheShort, fetchSync } from "@shopify/hydrogen";
2
2
  import { Narrow as NarrowUtil } from "@fast-simon/utilities";
3
3
  const ENDPOINT = "https://api.fastsimon.com";
4
4
  export const CategoriesNavigation = {
5
- get: ({ categoryID, page = 1, sortBy = "relevency", facets = true, narrow, productsPerPage, withProductAttributes = true, searchWithinResultsQuery, storeID, UUID, }) => {
5
+ get: ({ categoryID, page = 1, sortBy = "relevency", facets = true, narrow, productsPerPage, withProductAttributes = true, searchWithinResultsQuery, storeID, UUID, personalization, }) => {
6
6
  const narrowParm = narrow ? NarrowUtil.toServerNarrow(narrow) : undefined;
7
7
  const params = {
8
8
  category_id: categoryID,
@@ -14,7 +14,8 @@ export const CategoriesNavigation = {
14
14
  with_product_attributes: withProductAttributes ? withProductAttributes : undefined,
15
15
  search_within_search: searchWithinResultsQuery ? searchWithinResultsQuery : undefined,
16
16
  UUID: UUID,
17
- store_id: storeID
17
+ store_id: storeID,
18
+ ...(personalization ?? {})
18
19
  };
19
20
  const url = `${ENDPOINT}/categories_navigation?${Object.keys(params).filter(key => params[key] !== undefined).map(key => key + '=' + params[key]).join('&')}`;
20
21
  const data = fetchSync(url, {
@@ -24,11 +25,11 @@ export const CategoriesNavigation = {
24
25
  return {
25
26
  data: data,
26
27
  getFacetsOnly: (facets && !data.facets_completed) ? () => {
27
- return CategoriesNavigation.getFacetsOnly({ categoryID, page, sortBy, narrow, productsPerPage, searchWithinResultsQuery, UUID, storeID });
28
+ return CategoriesNavigation.getFacetsOnly({ categoryID, page, sortBy, narrow, productsPerPage, searchWithinResultsQuery, UUID, storeID, personalization });
28
29
  } : undefined
29
30
  };
30
31
  },
31
- getFacetsOnly: ({ categoryID, page = 1, sortBy = "relevency", narrow, productsPerPage, searchWithinResultsQuery, storeID, UUID }) => {
32
+ getFacetsOnly: ({ categoryID, page = 1, sortBy = "relevency", narrow, productsPerPage, searchWithinResultsQuery, storeID, UUID, personalization, }) => {
32
33
  const narrowParm = narrow ? NarrowUtil.toServerNarrow(narrow) : undefined;
33
34
  const params = {
34
35
  category_id: categoryID,
@@ -39,7 +40,8 @@ export const CategoriesNavigation = {
39
40
  narrow: JSON.stringify(narrowParm),
40
41
  search_within_search: searchWithinResultsQuery ? searchWithinResultsQuery : undefined,
41
42
  UUID: UUID,
42
- store_id: storeID
43
+ store_id: storeID,
44
+ ...(personalization ?? {})
43
45
  };
44
46
  const url = `${ENDPOINT}/categories_navigation?${Object.keys(params).filter(key => params[key] !== undefined).map(key => key + '=' + params[key]).join('&')}`;
45
47
  const data = fetchSync(url, {
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@fast-simon/shopify-hydrogen",
3
- "version": "1.0.7-beta.0",
3
+ "version": "1.0.8-beta.1",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "build": "npm run build:plugin:cjs && npm run build:esm",
7
7
  "build:plugin:cjs": "tsc --p tsconfig.plugin.cjs.json",
8
- "build:esm": "tsc --p tsconfig.esm.json"
8
+ "build:esm": "tsc --p tsconfig.esm.json",
9
+ "test": "vitest run",
10
+ "test:watch": "vitest"
9
11
  },
10
12
  "author": "",
11
13
  "license": "ISC",
@@ -15,7 +17,7 @@
15
17
  "devDependencies": {
16
18
  "@shopify/hydrogen": "^1.2.0",
17
19
  "@types/react": "^18.0.16",
18
- "@types/node": "17.0.35",
20
+ "@types/node": "^20.0.0",
19
21
  "@types/react-dom": "^18.0.5",
20
22
  "rollup": "^2.77.2",
21
23
  "rollup-plugin-dts": "^4.2.2",
@@ -23,7 +25,9 @@
23
25
  "vite": "^2.9.14",
24
26
  "type-fest": "^2.13.0",
25
27
  "@vitejs/plugin-react": "^1.3.0",
26
- "copyfiles": "2.4.1"
28
+ "copyfiles": "2.4.1",
29
+ "vitest": "^3.0.0",
30
+ "jsdom": "^26.0.0"
27
31
  },
28
32
  "main": "dist/esnext/index.js",
29
33
  "files": [