@okendo/shopify-hydrogen 2.2.6 → 2.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- > Note: this package is to be used on stores built with Hydrogen v2, based on Remix. If your store is built with the deprecated Hydrogen v1, please use the [version 1](https://www.npmjs.com/package/@okendo/shopify-hydrogen/v/1.3.0) of this package.
1
+ > Note: this package is to be used on stores built with Hydrogen v2, based on Remix. If your store is built with the deprecated Hydrogen v1, please use the [version 1](https://www.npmjs.com/package/@okendo/shopify-hydrogen/v/1.6.6) of this package.
2
2
 
3
3
  # Okendo Hydrogen 2 (Remix) React Components
4
4
 
@@ -15,7 +15,7 @@ This package brings [Okendo's review widgets](https://www.okendo.io/blog/widget-
15
15
 
16
16
  Our demo store, which is based on the demo store provided by Shopify, can be found [here](https://github.com/okendo/okendo-shopify-hydrogen-demo).
17
17
 
18
- > Note: there have been multiple versions of Shopify's Hydrogen demo store. If your project is based on an old version of it, consult the history of our demo store's repository.
18
+ > Note: there have been multiple versions of Shopify's Hydrogen demo store. If your project is based on an old version of it, consult the [history of our demo store's repository](https://github.com/okendo/okendo-shopify-hydrogen-demo/commits/master/).
19
19
 
20
20
  ## Exposition of Shopify Metafields <a id="expose-shopify-metafields" name="expose-shopify-metafields"></a>
21
21
 
@@ -42,7 +42,6 @@ unauthenticated_read_product_tags
42
42
 
43
43
  Follow the instructions on [this page](https://help.shopify.com/en/manual/apps/app-types/custom-apps#create-and-install-a-custom-app) to create it.
44
44
 
45
-
46
45
  #### Using Curl
47
46
 
48
47
  Open a new terminal or PowerShell window, then:
@@ -56,22 +55,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
56
55
  -H 'X-Shopify-Access-Token: {access_token}' \
57
56
  -d '
58
57
  mutation {
59
- metafieldDefinitionCreate(
60
- definition: {
61
- name: "WidgetPreRenderStyleTags"
62
- namespace: "$app:review"
63
- key: "widget_pre_render_style_tags"
64
- type: "multi_line_text_field"
65
- ownerType: SHOP
66
- access: {
67
- admin: PUBLIC_READ
68
- storefront: PUBLIC_READ
69
- }
70
- }
71
- ) {
72
- createdDefinition { id name }
73
- userErrors { field message code }
74
- }
58
+ metafieldDefinitionCreate(
59
+ definition: {
60
+ name: "WidgetPreRenderStyleTags"
61
+ namespace: "$app:reviews"
62
+ key: "widget_pre_render_style_tags"
63
+ type: "multi_line_text_field"
64
+ ownerType: SHOP
65
+ access: {
66
+ admin: PUBLIC_READ
67
+ storefront: PUBLIC_READ
68
+ }
69
+ }
70
+ ) {
71
+ createdDefinition { id name }
72
+ userErrors { field message code }
73
+ }
75
74
  }
76
75
  '
77
76
  ```
@@ -85,22 +84,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
85
84
  -H 'X-Shopify-Access-Token: {access_token}' \
86
85
  -d '
87
86
  mutation {
88
- metafieldDefinitionCreate(
89
- definition: {
90
- name: "WidgetPreRenderBodyStyleTags"
91
- namespace: "$app:review"
92
- key: "widget_pre_render_body_style_tags"
93
- type: "multi_line_text_field"
94
- ownerType: SHOP
95
- access: {
96
- admin: PUBLIC_READ
97
- storefront: PUBLIC_READ
98
- }
99
- }
100
- ) {
101
- createdDefinition { id name }
102
- userErrors { field message code }
103
- }
87
+ metafieldDefinitionCreate(
88
+ definition: {
89
+ name: "WidgetPreRenderBodyStyleTags"
90
+ namespace: "$app:reviews"
91
+ key: "widget_pre_render_body_style_tags"
92
+ type: "multi_line_text_field"
93
+ ownerType: SHOP
94
+ access: {
95
+ admin: PUBLIC_READ
96
+ storefront: PUBLIC_READ
97
+ }
98
+ }
99
+ ) {
100
+ createdDefinition { id name }
101
+ userErrors { field message code }
102
+ }
104
103
  }
105
104
  '
106
105
  ```
@@ -114,22 +113,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
114
113
  -H 'X-Shopify-Access-Token: {access_token}' \
115
114
  -d '
116
115
  mutation {
117
- metafieldDefinitionCreate(
118
- definition: {
119
- name: "ReviewsWidgetSnippet"
120
- namespace: "$app:reviews"
121
- key: "reviews_widget_snippet"
122
- type: "multi_line_text_field"
123
- ownerType: PRODUCT
124
- access: {
125
- admin: PUBLIC_READ
126
- storefront: PUBLIC_READ
127
- }
128
- }
129
- ) {
130
- createdDefinition { id name }
131
- userErrors { field message code }
132
- }
116
+ metafieldDefinitionCreate(
117
+ definition: {
118
+ name: "ReviewsWidgetSnippet"
119
+ namespace: "$app:reviews"
120
+ key: "reviews_widget_snippet"
121
+ type: "multi_line_text_field"
122
+ ownerType: PRODUCT
123
+ access: {
124
+ admin: PUBLIC_READ
125
+ storefront: PUBLIC_READ
126
+ }
127
+ }
128
+ ) {
129
+ createdDefinition { id name }
130
+ userErrors { field message code }
131
+ }
133
132
  }
134
133
  '
135
134
  ```
@@ -143,22 +142,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
143
142
  -H 'X-Shopify-Access-Token: {access_token}' \
144
143
  -d '
145
144
  mutation {
146
- metafieldDefinitionCreate(
147
- definition: {
148
- name: "StarRatingSnippet"
149
- namespace: "$app:reviews"
150
- key: "star_rating_snippet"
151
- type: "multi_line_text_field"
152
- ownerType: PRODUCT
153
- access: {
154
- admin: PUBLIC_READ
155
- storefront: PUBLIC_READ
156
- }
157
- }
158
- ) {
159
- createdDefinition { id name }
160
- userErrors { field message code }
161
- }
145
+ metafieldDefinitionCreate(
146
+ definition: {
147
+ name: "StarRatingSnippet"
148
+ namespace: "$app:reviews"
149
+ key: "star_rating_snippet"
150
+ type: "multi_line_text_field"
151
+ ownerType: PRODUCT
152
+ access: {
153
+ admin: PUBLIC_READ
154
+ storefront: PUBLIC_READ
155
+ }
156
+ }
157
+ ) {
158
+ createdDefinition { id name }
159
+ userErrors { field message code }
160
+ }
162
161
  }
163
162
  '
164
163
  ```
@@ -172,22 +171,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
172
171
  -H 'X-Shopify-Access-Token: {access_token}' \
173
172
  -d '
174
173
  mutation {
175
- metafieldDefinitionCreate(
176
- definition: {
177
- name: "ReviewCount"
178
- namespace: "$app:reviews"
179
- key: "review_count"
180
- type: "number_integer"
181
- ownerType: PRODUCT
182
- access: {
183
- admin: PUBLIC_READ
184
- storefront: PUBLIC_READ
185
- }
186
- }
187
- ) {
188
- createdDefinition { id name }
189
- userErrors { field message code }
190
- }
174
+ metafieldDefinitionCreate(
175
+ definition: {
176
+ name: "ReviewCount"
177
+ namespace: "$app:reviews"
178
+ key: "review_count"
179
+ type: "number_integer"
180
+ ownerType: PRODUCT
181
+ access: {
182
+ admin: PUBLIC_READ
183
+ storefront: PUBLIC_READ
184
+ }
185
+ }
186
+ ) {
187
+ createdDefinition { id name }
188
+ userErrors { field message code }
189
+ }
191
190
  }
192
191
  '
193
192
  ```
@@ -201,22 +200,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
201
200
  -H 'X-Shopify-Access-Token: {access_token}' \
202
201
  -d '
203
202
  mutation {
204
- metafieldDefinitionCreate(
205
- definition: {
206
- name: "AverageRating"
207
- namespace: "$app:reviews"
208
- key: "average_rating"
209
- type: "rating"
210
- ownerType: PRODUCT
211
- access: {
212
- admin: PUBLIC_READ
213
- storefront: PUBLIC_READ
214
- }
215
- }
216
- ) {
217
- createdDefinition { id name }
218
- userErrors { field message code }
219
- }
203
+ metafieldDefinitionCreate(
204
+ definition: {
205
+ name: "AverageRating"
206
+ namespace: "$app:reviews"
207
+ key: "average_rating"
208
+ type: "rating"
209
+ ownerType: PRODUCT
210
+ access: {
211
+ admin: PUBLIC_READ
212
+ storefront: PUBLIC_READ
213
+ }
214
+ }
215
+ ) {
216
+ createdDefinition { id name }
217
+ userErrors { field message code }
218
+ }
220
219
  }
221
220
  '
222
221
  ```
@@ -232,29 +231,26 @@ Open your GraphQL IDE (such as Postman) and make `POST` requests with the follow
232
231
 
233
232
  ```graphql
234
233
  mutation {
235
- metafieldDefinitionCreate(
236
- definition: {
237
- name: "WidgetPreRenderStyleTags"
238
- namespace: "$app:reviews"
239
- key: "widget_pre_render_style_tags"
240
- type: "multi_line_text_field"
241
- ownerType: SHOP
242
- access: {
243
- admin: PUBLIC_READ
244
- storefront: PUBLIC_READ
245
- }
246
- }
247
- ) {
248
- createdDefinition {
249
- id
250
- name
251
- }
252
- userErrors {
253
- field
254
- message
255
- code
256
- }
257
- }
234
+ metafieldDefinitionCreate(
235
+ definition: {
236
+ name: "WidgetPreRenderStyleTags"
237
+ namespace: "$app:reviews"
238
+ key: "widget_pre_render_style_tags"
239
+ type: "multi_line_text_field"
240
+ ownerType: SHOP
241
+ access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
242
+ }
243
+ ) {
244
+ createdDefinition {
245
+ id
246
+ name
247
+ }
248
+ userErrors {
249
+ field
250
+ message
251
+ code
252
+ }
253
+ }
258
254
  }
259
255
  ```
260
256
 
@@ -262,29 +258,26 @@ mutation {
262
258
 
263
259
  ```graphql
264
260
  mutation {
265
- metafieldDefinitionCreate(
266
- definition: {
267
- name: "WidgetPreRenderBodyStyleTags"
268
- namespace: "$app:reviews"
269
- key: "widget_pre_render_body_style_tags"
270
- type: "multi_line_text_field"
271
- ownerType: SHOP
272
- access: {
273
- admin: PUBLIC_READ
274
- storefront: PUBLIC_READ
275
- }
276
- }
277
- ) {
278
- createdDefinition {
279
- id
280
- name
281
- }
282
- userErrors {
283
- field
284
- message
285
- code
286
- }
287
- }
261
+ metafieldDefinitionCreate(
262
+ definition: {
263
+ name: "WidgetPreRenderBodyStyleTags"
264
+ namespace: "$app:reviews"
265
+ key: "widget_pre_render_body_style_tags"
266
+ type: "multi_line_text_field"
267
+ ownerType: SHOP
268
+ access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
269
+ }
270
+ ) {
271
+ createdDefinition {
272
+ id
273
+ name
274
+ }
275
+ userErrors {
276
+ field
277
+ message
278
+ code
279
+ }
280
+ }
288
281
  }
289
282
  ```
290
283
 
@@ -292,29 +285,26 @@ mutation {
292
285
 
293
286
  ```graphql
294
287
  mutation {
295
- metafieldDefinitionCreate(
296
- definition: {
297
- name: "ReviewsWidgetSnippet"
298
- namespace: "$app:reviews"
299
- key: "reviews_widget_snippet"
300
- type: "multi_line_text_field"
301
- ownerType: PRODUCT
302
- access: {
303
- admin: PUBLIC_READ
304
- storefront: PUBLIC_READ
305
- }
306
- }
307
- ) {
308
- createdDefinition {
309
- id
310
- name
311
- }
312
- userErrors {
313
- field
314
- message
315
- code
316
- }
317
- }
288
+ metafieldDefinitionCreate(
289
+ definition: {
290
+ name: "ReviewsWidgetSnippet"
291
+ namespace: "$app:reviews"
292
+ key: "reviews_widget_snippet"
293
+ type: "multi_line_text_field"
294
+ ownerType: PRODUCT
295
+ access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
296
+ }
297
+ ) {
298
+ createdDefinition {
299
+ id
300
+ name
301
+ }
302
+ userErrors {
303
+ field
304
+ message
305
+ code
306
+ }
307
+ }
318
308
  }
319
309
  ```
320
310
 
@@ -322,29 +312,26 @@ mutation {
322
312
 
323
313
  ```graphql
324
314
  mutation {
325
- metafieldDefinitionCreate(
326
- definition: {
327
- name: "StarRatingSnippet"
328
- namespace: "$app:reviews"
329
- key: "star_rating_snippet"
330
- type: "multi_line_text_field"
331
- ownerType: PRODUCT
332
- access: {
333
- admin: PUBLIC_READ
334
- storefront: PUBLIC_READ
335
- }
336
- }
337
- ) {
338
- createdDefinition {
339
- id
340
- name
341
- }
342
- userErrors {
343
- field
344
- message
345
- code
346
- }
347
- }
315
+ metafieldDefinitionCreate(
316
+ definition: {
317
+ name: "StarRatingSnippet"
318
+ namespace: "$app:reviews"
319
+ key: "star_rating_snippet"
320
+ type: "multi_line_text_field"
321
+ ownerType: PRODUCT
322
+ access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
323
+ }
324
+ ) {
325
+ createdDefinition {
326
+ id
327
+ name
328
+ }
329
+ userErrors {
330
+ field
331
+ message
332
+ code
333
+ }
334
+ }
348
335
  }
349
336
  ```
350
337
 
@@ -352,29 +339,26 @@ mutation {
352
339
 
353
340
  ```graphql
354
341
  mutation {
355
- metafieldDefinitionCreate(
356
- definition: {
357
- name: "ReviewCount"
358
- namespace: "$app:reviews"
359
- key: "review_count"
360
- type: "number_integer"
361
- ownerType: PRODUCT
362
- access: {
363
- admin: PUBLIC_READ
364
- storefront: PUBLIC_READ
365
- }
366
- }
367
- ) {
368
- createdDefinition {
369
- id
370
- name
371
- }
372
- userErrors {
373
- field
374
- message
375
- code
376
- }
377
- }
342
+ metafieldDefinitionCreate(
343
+ definition: {
344
+ name: "ReviewCount"
345
+ namespace: "$app:reviews"
346
+ key: "review_count"
347
+ type: "number_integer"
348
+ ownerType: PRODUCT
349
+ access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
350
+ }
351
+ ) {
352
+ createdDefinition {
353
+ id
354
+ name
355
+ }
356
+ userErrors {
357
+ field
358
+ message
359
+ code
360
+ }
361
+ }
378
362
  }
379
363
  ```
380
364
 
@@ -382,29 +366,26 @@ mutation {
382
366
 
383
367
  ```graphql
384
368
  mutation {
385
- metafieldDefinitionCreate(
386
- definition: {
387
- name: "AverageRating"
388
- namespace: "$app:reviews"
389
- key: "average_rating"
390
- type: "rating"
391
- ownerType: PRODUCT
392
- access: {
393
- admin: PUBLIC_READ
394
- storefront: PUBLIC_READ
395
- }
396
- }
397
- ) {
398
- createdDefinition {
399
- id
400
- name
401
- }
402
- userErrors {
403
- field
404
- message
405
- code
406
- }
407
- }
369
+ metafieldDefinitionCreate(
370
+ definition: {
371
+ name: "AverageRating"
372
+ namespace: "$app:reviews"
373
+ key: "average_rating"
374
+ type: "rating"
375
+ ownerType: PRODUCT
376
+ access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
377
+ }
378
+ ) {
379
+ createdDefinition {
380
+ id
381
+ name
382
+ }
383
+ userErrors {
384
+ field
385
+ message
386
+ code
387
+ }
388
+ }
408
389
  }
409
390
  ```
410
391
 
@@ -463,9 +444,9 @@ Open `app/root.tsx` and add the following import:
463
444
 
464
445
  ```ts
465
446
  import {
466
- OkendoProvider,
467
- getOkendoProviderData,
468
- } from "@okendo/shopify-hydrogen";
447
+ OkendoProvider,
448
+ getOkendoProviderData,
449
+ } from '@okendo/shopify-hydrogen';
469
450
  ```
470
451
 
471
452
  Locate the `loader` function, append `okendoProviderData` to the returned data as shown below, and set `subscriberId` to your Okendo subscriber ID.
@@ -473,40 +454,49 @@ Locate the `loader` function, append `okendoProviderData` to the returned data a
473
454
  As explained above, set `okendoProviderData` to either `getOkendoProviderData(...)`, or `await getOkendoProviderData(...)`:
474
455
 
475
456
  ```ts
476
- return defer(
477
- {
478
- ...
479
- okendoProviderData: /* place `await` here if you want server-rendered widgets */ getOkendoProviderData({
480
- context,
481
- subscriberId: "<your-okendo-subscriber-id>",
482
- }),
483
- },
484
- );
457
+ return defer({
458
+ // ...
459
+ okendoProviderData:
460
+ /* place `await` here if you want server-rendered widgets */ getOkendoProviderData(
461
+ {
462
+ context: args.context,
463
+ subscriberId: '<your-okendo-subscriber-id>',
464
+ },
465
+ ),
466
+ });
485
467
  ```
486
468
 
487
- Locate the `App` function, add the `meta` tag `oke:subscriber_id` to `head`, and place your Okendo subscriber ID in its content:
469
+ Locate the `Layout` component, add the `meta` tag `oke:subscriber_id` to `head`, and place your Okendo subscriber ID in its content:
488
470
 
489
- ```ts
471
+ ```tsx
490
472
  <head>
491
- <meta charSet="utf-8" />
492
- <meta name="viewport" content="width=device-width,initial-scale=1" />
493
- <meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
494
- ...
473
+ <meta charSet="utf-8" />
474
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
475
+ <meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
476
+ ...
477
+ </head>
495
478
  ```
496
479
 
497
480
  Append `OkendoProvider` to `body`, and pass it the promise — or the data — returned by `getOkendoProviderData`. If Content Security Policy is active in your project, you also need to provide the `nonce` (available with `const nonce = useNonce()` in Shopify's Hydrogen demo store):
498
481
 
499
482
  ```tsx
500
- ...
501
483
  <body>
502
- <OkendoProvider
503
- nonce={nonce}
504
- okendoProviderData={data.okendoProviderData}
505
- >
506
- ...
507
- </OkendoProvider>
484
+ {data ? (
485
+ <OkendoProvider nonce={nonce} okendoProviderData={data.okendoProviderData}>
486
+ <Analytics.Provider
487
+ cart={data.cart}
488
+ shop={data.shop}
489
+ consent={data.consent}
490
+ >
491
+ <PageLayout {...data}>{children}</PageLayout>
492
+ </Analytics.Provider>
493
+ </OkendoProvider>
494
+ ) : (
495
+ children
496
+ )}
497
+ <ScrollRestoration nonce={nonce} />
498
+ <Scripts nonce={nonce} />
508
499
  </body>
509
- ...
510
500
  ```
511
501
 
512
502
  ### `app/entry.server.tsx`
@@ -518,110 +508,108 @@ Note that it's necessary to to add the default values (`'self'`, etc.) when [ext
518
508
 
519
509
  ```ts
520
510
  const { nonce, header, NonceProvider } = createContentSecurityPolicy({
521
- defaultSrc: [
522
- "'self'",
523
- "localhost:*",
524
- "https://cdn.shopify.com",
525
- "https://www.google.com",
526
- "https://www.gstatic.com",
527
- "https://d3hw6dc1ow8pp2.cloudfront.net",
528
- "https://d3g5hqndtiniji.cloudfront.net",
529
- "https://dov7r31oq5dkj.cloudfront.net",
530
- "https://cdn-static.okendo.io",
531
- "https://surveys.okendo.io",
532
- "https://api.okendo.io",
533
- "data:",
534
- ],
535
- imgSrc: [
536
- "'self'",
537
- "https://cdn.shopify.com",
538
- "data:",
539
- "https://d3hw6dc1ow8pp2.cloudfront.net",
540
- "https://d3g5hqndtiniji.cloudfront.net",
541
- "https://dov7r31oq5dkj.cloudfront.net",
542
- "https://cdn-static.okendo.io",
543
- "https://surveys.okendo.io"
544
- ],
545
- mediaSrc: [
546
- "'self'",
547
- "https://d3hw6dc1ow8pp2.cloudfront.net",
548
- "https://d3g5hqndtiniji.cloudfront.net",
549
- "https://dov7r31oq5dkj.cloudfront.net",
550
- "https://cdn-static.okendo.io"
551
- ],
552
- styleSrcElem: [
553
- "'self'",
554
- "'unsafe-inline'",
555
- "https://cdn.shopify.com",
556
- "https://fonts.googleapis.com",
557
- "https://fonts.gstatic.com",
558
- "https://d3hw6dc1ow8pp2.cloudfront.net",
559
- "https://cdn-static.okendo.io",
560
- "https://surveys.okendo.io"
561
- ],
562
- scriptSrc: [
563
- "'self'",
564
- "https://cdn.shopify.com",
565
- "https://d3hw6dc1ow8pp2.cloudfront.net",
566
- "https://dov7r31oq5dkj.cloudfront.net",
567
- "https://cdn-static.okendo.io",
568
- "https://surveys.okendo.io",
569
- "https://api.okendo.io",
570
- "https://www.google.com",
571
- "https://www.gstatic.com"
572
- ],
573
- fontSrc: [
574
- "'self'",
575
- "https://fonts.gstatic.com",
576
- "https://d3hw6dc1ow8pp2.cloudfront.net",
577
- "https://dov7r31oq5dkj.cloudfront.net",
578
- "https://cdn.shopify.com",
579
- "https://cdn-static.okendo.io",
580
- "https://surveys.okendo.io"
581
- ],
582
- connectSrc: [
583
- "'self'",
584
- "https://monorail-edge.shopifysvc.com",
585
- "localhost:*",
586
- "ws://localhost:*",
587
- "ws://127.0.0.1:*",
588
- "https://api.okendo.io",
589
- "https://cdn-static.okendo.io",
590
- "https://surveys.okendo.io",
591
- "https://api.raygun.com",
592
- "https://www.google.com",
593
- "https://www.gstatic.com",
594
- ],
595
- frameSrc: [
596
- "https://www.google.com",
597
- "https://www.gstatic.com"
598
- ]
511
+ shop: {
512
+ checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
513
+ storeDomain: context.env.PUBLIC_STORE_DOMAIN,
514
+ },
515
+ defaultSrc: [
516
+ "'self'",
517
+ 'localhost:*',
518
+ 'https://cdn.shopify.com',
519
+ 'https://www.google.com',
520
+ 'https://www.gstatic.com',
521
+ 'https://d3hw6dc1ow8pp2.cloudfront.net',
522
+ 'https://d3g5hqndtiniji.cloudfront.net',
523
+ 'https://dov7r31oq5dkj.cloudfront.net',
524
+ 'https://cdn-static.okendo.io',
525
+ 'https://surveys.okendo.io',
526
+ 'https://api.okendo.io',
527
+ 'data:',
528
+ ],
529
+ imgSrc: [
530
+ "'self'",
531
+ 'https://cdn.shopify.com',
532
+ 'data:',
533
+ 'https://d3hw6dc1ow8pp2.cloudfront.net',
534
+ 'https://d3g5hqndtiniji.cloudfront.net',
535
+ 'https://dov7r31oq5dkj.cloudfront.net',
536
+ 'https://cdn-static.okendo.io',
537
+ 'https://surveys.okendo.io',
538
+ ],
539
+ mediaSrc: [
540
+ "'self'",
541
+ 'https://d3hw6dc1ow8pp2.cloudfront.net',
542
+ 'https://d3g5hqndtiniji.cloudfront.net',
543
+ 'https://dov7r31oq5dkj.cloudfront.net',
544
+ 'https://cdn-static.okendo.io',
545
+ ],
546
+ styleSrc: [
547
+ "'self'",
548
+ "'unsafe-inline'",
549
+ 'https://cdn.shopify.com',
550
+ 'https://fonts.googleapis.com',
551
+ 'https://fonts.gstatic.com',
552
+ 'https://d3hw6dc1ow8pp2.cloudfront.net',
553
+ 'https://cdn-static.okendo.io',
554
+ 'https://surveys.okendo.io',
555
+ ],
556
+ scriptSrc: [
557
+ "'self'",
558
+ 'https://cdn.shopify.com',
559
+ 'https://d3hw6dc1ow8pp2.cloudfront.net',
560
+ 'https://dov7r31oq5dkj.cloudfront.net',
561
+ 'https://cdn-static.okendo.io',
562
+ 'https://surveys.okendo.io',
563
+ 'https://api.okendo.io',
564
+ 'https://www.google.com',
565
+ 'https://www.gstatic.com',
566
+ ],
567
+ fontSrc: [
568
+ "'self'",
569
+ 'https://fonts.gstatic.com',
570
+ 'https://d3hw6dc1ow8pp2.cloudfront.net',
571
+ 'https://dov7r31oq5dkj.cloudfront.net',
572
+ 'https://cdn.shopify.com',
573
+ 'https://cdn-static.okendo.io',
574
+ 'https://surveys.okendo.io',
575
+ ],
576
+ connectSrc: [
577
+ "'self'",
578
+ 'https://monorail-edge.shopifysvc.com',
579
+ 'localhost:*',
580
+ 'ws://localhost:*',
581
+ 'ws://127.0.0.1:*',
582
+ 'https://api.okendo.io',
583
+ 'https://cdn-static.okendo.io',
584
+ 'https://surveys.okendo.io',
585
+ 'https://api.raygun.com',
586
+ 'https://www.google.com',
587
+ 'https://www.gstatic.com',
588
+ ],
589
+ frameSrc: ['https://www.google.com', 'https://www.gstatic.com'],
599
590
  });
600
591
  ```
601
592
 
602
593
  ### `app/routes/_index.tsx`
603
594
 
604
- Add the following imports:
595
+ Add the following import:
605
596
 
606
597
  ```ts
607
- import {
608
- OkendoStarRating,
609
- type WithOkendoStarRatingSnippet,
610
- } from "@okendo/shopify-hydrogen";
598
+ import { OkendoStarRating } from '@okendo/shopify-hydrogen';
611
599
  ```
612
600
 
613
601
  Add the following block just before the `RECOMMENDED_PRODUCTS_QUERY` GraphQL query:
614
602
 
615
603
  ```ts
616
604
  const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
617
- fragment OkendoStarRatingSnippet on Product {
618
- okendoStarRatingSnippet: metafield(
619
- namespace: "okendo"
620
- key: "StarRatingSnippet"
621
- ) {
622
- value
623
- }
624
- }
605
+ fragment OkendoStarRatingSnippet on Product {
606
+ okendoStarRatingSnippet: metafield(
607
+ namespace: "okendo"
608
+ key: "StarRatingSnippet"
609
+ ) {
610
+ value
611
+ }
612
+ }
625
613
  ` as const;
626
614
  ```
627
615
 
@@ -629,74 +617,56 @@ Then append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}` and `...OkendoStarRatingSni
629
617
 
630
618
  ```ts
631
619
  const RECOMMENDED_PRODUCTS_QUERY = `#graphql
632
- ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
633
- fragment RecommendedProduct on Product {
634
- id
635
- title
636
- handle
637
- priceRange {
638
- minVariantPrice {
639
- amount
640
- currencyCode
641
- }
642
- }
643
- images(first: 1) {
644
- nodes {
645
- id
646
- url
647
- altText
648
- width
649
- height
650
- }
651
- }
652
- ...OkendoStarRatingSnippet
653
- }
654
- query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
655
- @inContext(country: $country, language: $language) {
656
- products(first: 4, sortKey: UPDATED_AT, reverse: true) {
657
- nodes {
658
- ...RecommendedProduct
659
- }
660
- }
661
- }
620
+ ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
621
+ fragment RecommendedProduct on Product {
622
+ id
623
+ title
624
+ handle
625
+ priceRange {
626
+ minVariantPrice {
627
+ amount
628
+ currencyCode
629
+ }
630
+ }
631
+ images(first: 1) {
632
+ nodes {
633
+ id
634
+ url
635
+ altText
636
+ width
637
+ height
638
+ }
639
+ }
640
+ ...OkendoStarRatingSnippet
641
+ }
642
+ query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
643
+ @inContext(country: $country, language: $language) {
644
+ products(first: 4, sortKey: UPDATED_AT, reverse: true) {
645
+ nodes {
646
+ ...RecommendedProduct
647
+ }
648
+ }
649
+ }
662
650
  ` as const;
663
651
  ```
664
652
 
665
- Tweak the type of the `products` prop of `RecommendedProducts`:
666
-
667
- ```ts
668
- products: Promise<{
669
- products: {
670
- nodes: (RecommendedProductsQuery["products"]["nodes"][0] &
671
- WithOkendoStarRatingSnippet)[];
672
- };
673
- }>;
674
- ```
675
-
676
- Add `OkendoStarRating` to `RecommendedProducts`:
677
-
678
- ```tsx
679
- <OkendoStarRating
680
- productId={product.id}
681
- okendoStarRatingSnippet={product.okendoStarRatingSnippet}
682
- />
683
- ```
653
+ > Note: if you get a type error on `product`, restart the dev server to get the types (`storefrontapi.generated.d.ts`) regenerated from the GraphQL fragments.
684
654
 
685
- For instance, we can add it below the product title, like this:
655
+ Add `OkendoStarRating` to the `RecommendedProducts` component — for instance, we can add it below the product title, like this:
686
656
 
687
657
  ```tsx
688
658
  <Image
689
- data={product.images.nodes[0]}
690
- aspectRatio="1/1"
691
- sizes="(min-width: 45em) 20vw, 50vw"
659
+ data={product.images.nodes[0]}
660
+ aspectRatio="1/1"
661
+ sizes="(min-width: 45em) 20vw, 50vw"
692
662
  />
693
663
  <h4>{product.title}</h4>
694
664
  <OkendoStarRating
695
- productId={product.id}
696
- okendoStarRatingSnippet={product.okendoStarRatingSnippet}
665
+ productId={product.id}
666
+ okendoStarRatingSnippet={product.okendoStarRatingSnippet}
697
667
  />
698
668
  <small>
699
- <Money data={product.priceRange.minVariantPrice} />
669
+ <Money data={product.priceRange.minVariantPrice} />
700
670
  </small>
701
671
  ```
702
672
 
@@ -704,46 +674,41 @@ For instance, we can add it below the product title, like this:
704
674
 
705
675
  We now have the Okendo Star Rating widget visible on our page:
706
676
 
707
- ![Okendo's Star Rating widget](./okendo-star-rating-widget.png)
677
+ ![Okendo's Star Rating widget](./readme-res/okendo-star-rating-widget.png)
678
+
679
+ You can do the same changes to the files `app/routes/collections.$handle.tsx` and `app/routes/collections.all.tsx` to make the items' Okendo Star Rating visible on collection pages.
708
680
 
709
681
  ### `app/routes/products.$handle.tsx`
710
682
 
711
- Add the following imports:
683
+ Add the following import:
712
684
 
713
685
  ```ts
714
- import {
715
- OKENDO_PRODUCT_REVIEWS_FRAGMENT,
716
- OKENDO_PRODUCT_STAR_RATING_FRAGMENT,
717
- OkendoReviews,
718
- OkendoStarRating,
719
- type WithOkendoReviewsSnippet,
720
- type WithOkendoStarRatingSnippet,
721
- } from "@okendo/shopify-hydrogen";
686
+ import { OkendoReviews, OkendoStarRating } from '@okendo/shopify-hydrogen';
722
687
  ```
723
688
 
724
- Add the following block just before the `RECOMMENDED_PRODUCTS_QUERY` GraphQL query:
689
+ Add the following block just before the `PRODUCT_FRAGMENT` GraphQL query:
725
690
 
726
691
  ```ts
727
692
  const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
728
- fragment OkendoStarRatingSnippet on Product {
729
- okendoStarRatingSnippet: metafield(
730
- namespace: "okendo"
731
- key: "StarRatingSnippet"
732
- ) {
733
- value
734
- }
735
- }
693
+ fragment OkendoStarRatingSnippet on Product {
694
+ okendoStarRatingSnippet: metafield(
695
+ namespace: "okendo"
696
+ key: "StarRatingSnippet"
697
+ ) {
698
+ value
699
+ }
700
+ }
736
701
  ` as const;
737
702
 
738
703
  const OKENDO_PRODUCT_REVIEWS_FRAGMENT = `#graphql
739
- fragment OkendoReviewsSnippet on Product {
740
- okendoReviewsSnippet: metafield(
741
- namespace: "okendo"
742
- key: "ReviewsWidgetSnippet"
743
- ) {
744
- value
745
- }
746
- }
704
+ fragment OkendoReviewsSnippet on Product {
705
+ okendoReviewsSnippet: metafield(
706
+ namespace: "okendo"
707
+ key: "ReviewsWidgetSnippet"
708
+ ) {
709
+ value
710
+ }
711
+ }
747
712
  ` as const;
748
713
  ```
749
714
 
@@ -751,113 +716,86 @@ Then append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}`, `${OKENDO_PRODUCT_REVIEWS_
751
716
 
752
717
  ```ts
753
718
  const PRODUCT_FRAGMENT = `#graphql
754
- ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
755
- ${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
756
- fragment Product on Product {
757
- id
758
- title
759
- vendor
760
- handle
761
- descriptionHtml
762
- description
763
- options {
764
- name
765
- values
766
- }
767
- selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
768
- ...ProductVariant
769
- }
770
- variants(first: 1) {
771
- nodes {
772
- ...ProductVariant
773
- }
774
- }
775
- seo {
776
- description
777
- title
778
- }
779
- ...OkendoStarRatingSnippet
780
- ...OkendoReviewsSnippet
781
- }
782
- ${PRODUCT_VARIANT_FRAGMENT}
719
+ ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
720
+ ${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
721
+ fragment Product on Product {
722
+ id
723
+ title
724
+ vendor
725
+ handle
726
+ descriptionHtml
727
+ description
728
+ options {
729
+ name
730
+ values
731
+ }
732
+ selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
733
+ ...ProductVariant
734
+ }
735
+ variants(first: 1) {
736
+ nodes {
737
+ ...ProductVariant
738
+ }
739
+ }
740
+ seo {
741
+ description
742
+ title
743
+ }
744
+ ...OkendoStarRatingSnippet
745
+ ...OkendoReviewsSnippet
746
+ }
747
+ ${PRODUCT_VARIANT_FRAGMENT}
783
748
  ` as const;
784
749
  ```
785
750
 
786
- Add `OkendoReviews` to `Product`:
751
+ > Note: if you get a type error on `product`, restart the dev server to get the types (`storefrontapi.generated.d.ts`) regenerated from the GraphQL fragments.
787
752
 
788
- ```tsx
789
- <OkendoReviews
790
- productId={product.id}
791
- okendoReviewsSnippet={product.okendoReviewsSnippet}
792
- />
793
- ```
794
-
795
- For instance, we can add it below the product section, like this:
753
+ Add `OkendoStarRating` and `OkendoReviews` to the `Product` component:
796
754
 
797
755
  ```tsx
798
756
  <>
799
- <div className="product">
800
- <ProductImage image={selectedVariant?.image} />
801
- <ProductMain
802
- selectedVariant={selectedVariant}
803
- product={product}
804
- variants={variants}
805
- />
806
- </div>
807
-
808
- <OkendoReviews
809
- productId={product.id}
810
- okendoReviewsSnippet={product.okendoReviewsSnippet}
811
- />
812
- </>
813
- ```
814
-
815
- > Note: if the widgets are rendered client-side (if you don't use `await` when calling `getOkendoProviderData`), you can provide your own placeholder by using the `placeholder` property of `OkendoReviews`.
816
-
817
- Tweak the type of the `product` prop of `ProductMain`:
818
-
819
- ```ts
820
- product: ProductFragment &
821
- WithOkendoStarRatingSnippet &
822
- WithOkendoReviewsSnippet;
823
- ```
824
-
825
- Add `OkendoStarRating` to `ProductMain`:
757
+ <div className="product">
758
+ <ProductImage image={selectedVariant?.image} />
759
+ <div className="product-main">
760
+ <h1>{title}</h1>
761
+ <OkendoStarRating
762
+ productId={product.id}
763
+ okendoStarRatingSnippet={product.okendoStarRatingSnippet}
764
+ />
765
+ <ProductPrice
766
+ price={selectedVariant?.price}
767
+ compareAtPrice={selectedVariant?.compareAtPrice}
768
+ />
769
+ ...
770
+ </div>
771
+ ...
772
+ </div>
826
773
 
827
- ```tsx
828
- <OkendoStarRating
829
- productId={product.id}
830
- okendoStarRatingSnippet={product.okendoStarRatingSnippet}
831
- />
774
+ <OkendoReviews
775
+ productId={product.id}
776
+ okendoReviewsSnippet={product.okendoReviewsSnippet}
777
+ />
778
+ </>
832
779
  ```
833
780
 
834
- For instance, we can add it below the product title, like this:
835
-
836
- ```tsx
837
- <div className="product-main">
838
- <h1>{title}</h1>
839
- <OkendoStarRating
840
- productId={product.id}
841
- okendoStarRatingSnippet={product.okendoStarRatingSnippet}
842
- />
843
- <ProductPrice selectedVariant={selectedVariant} />
844
- ```
781
+ > Note: if the widgets are rendered client-side (if you don't use `await` when calling `getOkendoProviderData`), you can provide your own placeholder by using the `placeholder` property of `OkendoStarRating` and `OkendoReviews`.
845
782
 
846
783
  We now have the Okendo Star Rating and Reviews widgets visible on our product page:
847
784
 
848
- ![Okendo's Star Rating and Reviews widgets](./okendo-star-rating-and-reviews-widgets.png)
785
+ ![Okendo's Star Rating and Reviews widgets](./readme-res/okendo-star-rating-and-reviews-widgets.png)
849
786
 
850
787
  ### All Reviews Widget - Client Side Only
851
- If you would like to include a copy of the Okendo Reviews Widget which displays all reviews for a given store (to be used on a reviews page for example), please add the OkendoReviewsWidget without supplying the `productId`.
852
788
 
853
- Please note the all reviews widget loads on the client not the server.
789
+ If you would like to include a copy of the Okendo Reviews Widget which displays all reviews for a given store (to be used on a reviews page for example), please add the `OkendoReviewsWidget` without supplying the `productId`.
790
+
791
+ Please note the all reviews widget loads on the client, not the server.
854
792
 
855
793
  ```tsx
856
794
  import { type MetaFunction } from '@remix-run/react';
857
795
  import { OkendoReviews } from '@okendo/shopify-hydrogen';
858
796
 
859
797
  export const meta: MetaFunction = () => {
860
- return [{title: `Hydrogen | Okendo All Reviews`}];
798
+ return [{ title: `Hydrogen | Okendo All Reviews` }];
861
799
  };
862
800
 
863
801
  export default function ReviewsPage() {
@@ -868,4 +806,4 @@ export default function ReviewsPage() {
868
806
  </div>
869
807
  );
870
808
  }
871
- ```
809
+ ```
package/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@okendo/shopify-hydrogen",
3
- "version": "2.2.6",
3
+ "version": "2.3.0",
4
4
  "description": "Okendo React components for Shopify Hydrogen 2 (Remix)",
5
5
  "author": "Okendo",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",
7
7
  "engines": {
8
- "node": ">=16.14"
8
+ "node": ">=18.0.0"
9
9
  },
10
10
  "main": "dist/cjs/index.js",
11
11
  "module": "dist/esm/index.js",
12
12
  "files": [
13
- "dist"
13
+ "dist",
14
+ "readme-res"
14
15
  ],
15
16
  "types": "dist/index.d.ts",
16
17
  "scripts": {