@okendo/shopify-hydrogen 2.1.4 → 2.1.6
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/LICENSE.txt +3 -3
- package/README.md +881 -635
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/types/internal/OkendoWidget/OkendoWidget.d.ts +5 -1
- package/dist/cjs/types/internal/usePrevious.d.ts +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/types/internal/OkendoWidget/OkendoWidget.d.ts +5 -1
- package/dist/esm/types/internal/usePrevious.d.ts +1 -0
- package/package.json +47 -47
package/README.md
CHANGED
|
@@ -1,635 +1,881 @@
|
|
|
1
|
-
> Note: this package is to be used on stores built with Hydrogen v2, based on Remix. If your store is built with the now deprecated Hydrogen v1, please use [version 1 of this package](https://www.npmjs.com/package/@okendo/shopify-hydrogen/v/1.3.0).
|
|
2
|
-
|
|
3
|
-
# Okendo Hydrogen 2 (Remix) React Components
|
|
4
|
-
|
|
5
|
-
This package brings [Okendo's review widgets](https://www.okendo.io/blog/widget-plus/) to a Shopify Hydrogen store.
|
|
6
|
-
|
|
7
|
-
## Requirements
|
|
8
|
-
|
|
9
|
-
- A Shopify store with the [**Okendo: Product Reviews & UCG**](https://apps.shopify.com/okendo-reviews) app installed and configured.
|
|
10
|
-
- For existing merchants, your store must be upgraded to Okendo's Widget Plus widgets. It is free to upgrade. For more information please [contact Okendo Support](mailto:support@okendo.io).
|
|
11
|
-
- A current Okendo subscription.
|
|
12
|
-
- A [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
### Exposing Metafields via GraphQL
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
`
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
1
|
+
> Note: this package is to be used on stores built with Hydrogen v2, based on Remix. If your store is built with the now deprecated Hydrogen v1, please use [version 1 of this package](https://www.npmjs.com/package/@okendo/shopify-hydrogen/v/1.3.0).
|
|
2
|
+
|
|
3
|
+
# Okendo Hydrogen 2 (Remix) React Components
|
|
4
|
+
|
|
5
|
+
This package brings [Okendo's review widgets](https://www.okendo.io/blog/widget-plus/) to a Shopify Hydrogen store.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- A Shopify store with the [**Okendo: Product Reviews & UCG**](https://apps.shopify.com/okendo-reviews) app installed and configured.
|
|
10
|
+
- For existing merchants, your store must be upgraded to Okendo's Widget Plus widgets. It is free to upgrade. For more information please [contact Okendo Support](mailto:support@okendo.io).
|
|
11
|
+
- A current Okendo subscription.
|
|
12
|
+
- A [Shopify Hydrogen](https://hydrogen.shopify.dev/) app.
|
|
13
|
+
|
|
14
|
+
## Demo Store
|
|
15
|
+
|
|
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
|
+
|
|
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 our version history to find out how to add Okendo to it.
|
|
19
|
+
|
|
20
|
+
## How it works
|
|
21
|
+
|
|
22
|
+
This package provides:
|
|
23
|
+
|
|
24
|
+
- one function: `getOkendoProviderData`
|
|
25
|
+
- three React components: `OkendoProvider`, `OkendoStarRating`, and `OkendoReviews`
|
|
26
|
+
|
|
27
|
+
The function `getOkendoProviderData` needs to be called in the `loader` function of the `root.tsx` file in the Hydrogen 2 store. The data is then retrieved in `App` through `useLoaderData` and provided to `OkendoProvider` which is added in the `body` of the HTML returned by `App`.
|
|
28
|
+
|
|
29
|
+
Then, the components `OkendoStarRating` and `OkendoReviews` can be added on the store pages. There are a few more bits of configuration to do, please see below.
|
|
30
|
+
|
|
31
|
+
## Expose Shopify Metafields <a id="expose-shopify-metafields" name="expose-shopify-metafields"></a>
|
|
32
|
+
|
|
33
|
+
Okendo Reviews use Product and Shop [metafields](https://shopify.dev/api/examples/metafields). You will need to expose these metafields so that they can be retrieved by your Hydrogen app.
|
|
34
|
+
|
|
35
|
+
At the moment, Shopify does not have a way of exposing Shop Metafields through their admin UI, so the preferred method is to [contact Okendo Support](mailto:support@okendo.io).
|
|
36
|
+
|
|
37
|
+
<details>
|
|
38
|
+
|
|
39
|
+
<summary>If you're a technical user however, you can click here and follow the method to expose the metafields via the storefront API.</summary>
|
|
40
|
+
|
|
41
|
+
### Exposing Metafields via GraphQL
|
|
42
|
+
|
|
43
|
+
You will need a **Storefront access token** with the following API access scopes:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
unauthenticated_read_content
|
|
47
|
+
unauthenticated_read_customers
|
|
48
|
+
unauthenticated_read_product_listings
|
|
49
|
+
unauthenticated_read_product_inventory
|
|
50
|
+
unauthenticated_read_product_pickup_locations
|
|
51
|
+
unauthenticated_read_product_tags
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
#### Note
|
|
57
|
+
|
|
58
|
+
Shopify is in the process of deprecating `metafieldStorefrontVisibilityCreate` in favour of `metafieldDefinitionCreate`. The following method uses `metafieldDefinitionCreate`. If you're having trouble with it, you will find below the deprecated method using `metafieldStorefrontVisibilityCreate`.
|
|
59
|
+
|
|
60
|
+
#### Using Curl
|
|
61
|
+
|
|
62
|
+
Open a new terminal or PowerShell window, then:
|
|
63
|
+
|
|
64
|
+
1. Run the following command to expose the `WidgetPreRenderStyleTags` shop metafield:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
curl -X POST \
|
|
68
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
69
|
+
-H 'Content-Type: application/graphql' \
|
|
70
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
71
|
+
-d '
|
|
72
|
+
mutation {
|
|
73
|
+
metafieldDefinitionCreate(
|
|
74
|
+
definition: {
|
|
75
|
+
name: "WidgetPreRenderStyleTags"
|
|
76
|
+
namespace: "okendo"
|
|
77
|
+
key: "WidgetPreRenderStyleTags"
|
|
78
|
+
type: "multi_line_text_field"
|
|
79
|
+
ownerType: SHOP
|
|
80
|
+
visibleToStorefrontApi: true
|
|
81
|
+
}
|
|
82
|
+
) {
|
|
83
|
+
createdDefinition { id name }
|
|
84
|
+
userErrors { field message code }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
2. Run the following command to expose the `WidgetPreRenderBodyStyleTags` shop metafield:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
curl -X POST \
|
|
94
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
95
|
+
-H 'Content-Type: application/graphql' \
|
|
96
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
97
|
+
-d '
|
|
98
|
+
mutation {
|
|
99
|
+
metafieldDefinitionCreate(
|
|
100
|
+
definition: {
|
|
101
|
+
name: "WidgetPreRenderBodyStyleTags"
|
|
102
|
+
namespace: "okendo"
|
|
103
|
+
key: "WidgetPreRenderBodyStyleTags"
|
|
104
|
+
type: "multi_line_text_field"
|
|
105
|
+
ownerType: SHOP
|
|
106
|
+
visibleToStorefrontApi: true
|
|
107
|
+
}
|
|
108
|
+
) {
|
|
109
|
+
createdDefinition { id name }
|
|
110
|
+
userErrors { field message code }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
3. Run the following command to expose the `ReviewsWidgetSnippet` product metafield:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
curl -X POST \
|
|
120
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
121
|
+
-H 'Content-Type: application/graphql' \
|
|
122
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
123
|
+
-d '
|
|
124
|
+
mutation {
|
|
125
|
+
metafieldDefinitionCreate(
|
|
126
|
+
definition: {
|
|
127
|
+
name: "ReviewsWidgetSnippet"
|
|
128
|
+
namespace: "okendo"
|
|
129
|
+
key: "ReviewsWidgetSnippet"
|
|
130
|
+
type: "multi_line_text_field"
|
|
131
|
+
ownerType: PRODUCT
|
|
132
|
+
visibleToStorefrontApi: true
|
|
133
|
+
}
|
|
134
|
+
) {
|
|
135
|
+
createdDefinition { id name }
|
|
136
|
+
userErrors { field message code }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
'
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
4. Run the following command to expose the `StarRatingSnippet` the product metafield:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
curl -X POST \
|
|
146
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
147
|
+
-H 'Content-Type: application/graphql' \
|
|
148
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
149
|
+
-d '
|
|
150
|
+
mutation {
|
|
151
|
+
metafieldDefinitionCreate(
|
|
152
|
+
definition: {
|
|
153
|
+
name: "StarRatingSnippet"
|
|
154
|
+
namespace: "okendo"
|
|
155
|
+
key: "StarRatingSnippet"
|
|
156
|
+
type: "multi_line_text_field"
|
|
157
|
+
ownerType: PRODUCT
|
|
158
|
+
visibleToStorefrontApi: true
|
|
159
|
+
}
|
|
160
|
+
) {
|
|
161
|
+
createdDefinition { id name }
|
|
162
|
+
userErrors { field message code }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
'
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Using GraphQL IDE
|
|
169
|
+
|
|
170
|
+
Open your GraphQL IDE (such as Postman) and make `POST` requests with the following details:
|
|
171
|
+
|
|
172
|
+
- **URL:** https://{shop}.myshopify.com/admin/api/2023-07/graphql.json
|
|
173
|
+
- **Headers:** - X-Shopify-Access-Token: {access_token} - Content-Type: application/json
|
|
174
|
+
|
|
175
|
+
1. Execute the following request to expose the `WidgetPreRenderStyleTags` shop metafield:
|
|
176
|
+
|
|
177
|
+
```graphql
|
|
178
|
+
mutation {
|
|
179
|
+
metafieldDefinitionCreate(
|
|
180
|
+
definition: {
|
|
181
|
+
name: "WidgetPreRenderStyleTags"
|
|
182
|
+
namespace: "okendo"
|
|
183
|
+
key: "WidgetPreRenderStyleTags"
|
|
184
|
+
type: "multi_line_text_field"
|
|
185
|
+
ownerType: SHOP
|
|
186
|
+
visibleToStorefrontApi: true
|
|
187
|
+
}
|
|
188
|
+
) {
|
|
189
|
+
createdDefinition {
|
|
190
|
+
id
|
|
191
|
+
name
|
|
192
|
+
}
|
|
193
|
+
userErrors {
|
|
194
|
+
field
|
|
195
|
+
message
|
|
196
|
+
code
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
2. Execute the following request to expose the `WidgetPreRenderBodyStyleTags` shop metafield:
|
|
203
|
+
|
|
204
|
+
```graphql
|
|
205
|
+
mutation {
|
|
206
|
+
metafieldDefinitionCreate(
|
|
207
|
+
definition: {
|
|
208
|
+
name: "WidgetPreRenderBodyStyleTags"
|
|
209
|
+
namespace: "okendo"
|
|
210
|
+
key: "WidgetPreRenderBodyStyleTags"
|
|
211
|
+
type: "multi_line_text_field"
|
|
212
|
+
ownerType: SHOP
|
|
213
|
+
visibleToStorefrontApi: true
|
|
214
|
+
}
|
|
215
|
+
) {
|
|
216
|
+
createdDefinition {
|
|
217
|
+
id
|
|
218
|
+
name
|
|
219
|
+
}
|
|
220
|
+
userErrors {
|
|
221
|
+
field
|
|
222
|
+
message
|
|
223
|
+
code
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
3. Execute the following request to expose the `ReviewsWidgetSnippet` product metafield:
|
|
230
|
+
|
|
231
|
+
```graphql
|
|
232
|
+
mutation {
|
|
233
|
+
metafieldDefinitionCreate(
|
|
234
|
+
definition: {
|
|
235
|
+
name: "ReviewsWidgetSnippet"
|
|
236
|
+
namespace: "okendo"
|
|
237
|
+
key: "ReviewsWidgetSnippet"
|
|
238
|
+
type: "multi_line_text_field"
|
|
239
|
+
ownerType: PRODUCT
|
|
240
|
+
visibleToStorefrontApi: true
|
|
241
|
+
}
|
|
242
|
+
) {
|
|
243
|
+
createdDefinition {
|
|
244
|
+
id
|
|
245
|
+
name
|
|
246
|
+
}
|
|
247
|
+
userErrors {
|
|
248
|
+
field
|
|
249
|
+
message
|
|
250
|
+
code
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
4. Execute the following request to expose the `StarRatingSnippet` the product metafield:
|
|
257
|
+
|
|
258
|
+
```graphql
|
|
259
|
+
mutation {
|
|
260
|
+
metafieldDefinitionCreate(
|
|
261
|
+
definition: {
|
|
262
|
+
name: "StarRatingSnippet"
|
|
263
|
+
namespace: "okendo"
|
|
264
|
+
key: "StarRatingSnippet"
|
|
265
|
+
type: "multi_line_text_field"
|
|
266
|
+
ownerType: PRODUCT
|
|
267
|
+
visibleToStorefrontApi: true
|
|
268
|
+
}
|
|
269
|
+
) {
|
|
270
|
+
createdDefinition {
|
|
271
|
+
id
|
|
272
|
+
name
|
|
273
|
+
}
|
|
274
|
+
userErrors {
|
|
275
|
+
field
|
|
276
|
+
message
|
|
277
|
+
code
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**References**
|
|
284
|
+
|
|
285
|
+
- [https://shopify.dev/api/examples/metafields#step-1-expose-metafields](https://shopify.dev/api/examples/metafields#step-1-expose-metafields)
|
|
286
|
+
- [https://shopify.dev/api/admin-graphql/2023-07/mutations/metafieldDefinitionCreate](https://shopify.dev/api/admin-graphql/2023-07/mutations/metafieldDefinitionCreate)
|
|
287
|
+
|
|
288
|
+
<details>
|
|
289
|
+
|
|
290
|
+
<summary>If you're having trouble with `metafieldDefinitionCreate`, click here to see the deprecated method, using `metafieldStorefrontVisibilityCreate`.</summary>
|
|
291
|
+
|
|
292
|
+
#### Using Curl
|
|
293
|
+
|
|
294
|
+
Open a new terminal or PowerShell window, then:
|
|
295
|
+
|
|
296
|
+
1. Run the following command to expose the `WidgetPreRenderStyleTags` shop metafield:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
curl -X POST \
|
|
300
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
301
|
+
-H 'Content-Type: application/graphql' \
|
|
302
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
303
|
+
-d '
|
|
304
|
+
mutation {
|
|
305
|
+
metafieldStorefrontVisibilityCreate(
|
|
306
|
+
input: {
|
|
307
|
+
namespace: "okendo"
|
|
308
|
+
key: "WidgetPreRenderStyleTags"
|
|
309
|
+
ownerType: SHOP
|
|
310
|
+
}
|
|
311
|
+
) {
|
|
312
|
+
metafieldStorefrontVisibility {
|
|
313
|
+
id
|
|
314
|
+
}
|
|
315
|
+
userErrors {
|
|
316
|
+
field
|
|
317
|
+
message
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
'
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
2. Run the following command to expose the `WidgetPreRenderBodyStyleTags` shop metafield:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
curl -X POST \
|
|
328
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
329
|
+
-H 'Content-Type: application/graphql' \
|
|
330
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
331
|
+
-d '
|
|
332
|
+
mutation {
|
|
333
|
+
metafieldStorefrontVisibilityCreate(
|
|
334
|
+
input: {
|
|
335
|
+
namespace: "okendo"
|
|
336
|
+
key: "WidgetPreRenderBodyStyleTags"
|
|
337
|
+
ownerType: SHOP
|
|
338
|
+
}
|
|
339
|
+
) {
|
|
340
|
+
metafieldStorefrontVisibility {
|
|
341
|
+
id
|
|
342
|
+
}
|
|
343
|
+
userErrors {
|
|
344
|
+
field
|
|
345
|
+
message
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
'
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
3. Run the following command to expose the `ReviewsWidgetSnippet` product metafield:
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
curl -X POST \
|
|
356
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
357
|
+
-H 'Content-Type: application/graphql' \
|
|
358
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
359
|
+
-d '
|
|
360
|
+
mutation {
|
|
361
|
+
metafieldStorefrontVisibilityCreate(
|
|
362
|
+
input: {
|
|
363
|
+
namespace: "okendo"
|
|
364
|
+
key: "ReviewsWidgetSnippet"
|
|
365
|
+
ownerType: PRODUCT
|
|
366
|
+
}
|
|
367
|
+
) {
|
|
368
|
+
metafieldStorefrontVisibility {
|
|
369
|
+
id
|
|
370
|
+
}
|
|
371
|
+
userErrors {
|
|
372
|
+
field
|
|
373
|
+
message
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
'
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
4. Run the following command to expose the `StarRatingSnippet` the product metafield:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
curl -X POST \
|
|
384
|
+
https://{shop}.myshopify.com/admin/api/2023-07/graphql.json \
|
|
385
|
+
-H 'Content-Type: application/graphql' \
|
|
386
|
+
-H 'X-Shopify-Access-Token: {access_token}' \
|
|
387
|
+
-d '
|
|
388
|
+
mutation {
|
|
389
|
+
metafieldStorefrontVisibilityCreate(
|
|
390
|
+
input: {
|
|
391
|
+
namespace: "okendo"
|
|
392
|
+
key: "StarRatingSnippet"
|
|
393
|
+
ownerType: PRODUCT
|
|
394
|
+
}
|
|
395
|
+
) {
|
|
396
|
+
metafieldStorefrontVisibility {
|
|
397
|
+
id
|
|
398
|
+
}
|
|
399
|
+
userErrors {
|
|
400
|
+
field
|
|
401
|
+
message
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
'
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Using GraphQL IDE
|
|
409
|
+
|
|
410
|
+
Open your GraphQL IDE (such as Postman) and make `POST` requests with the following details:
|
|
411
|
+
|
|
412
|
+
- **URL:** https://{shop}.myshopify.com/admin/api/2023-07/graphql.json
|
|
413
|
+
- **Headers:** - X-Shopify-Access-Token: {access_token} - Content-Type: application/json
|
|
414
|
+
|
|
415
|
+
1. Execute the following request to expose the `WidgetPreRenderStyleTags` shop metafield:
|
|
416
|
+
|
|
417
|
+
```graphql
|
|
418
|
+
mutation {
|
|
419
|
+
metafieldStorefrontVisibilityCreate(
|
|
420
|
+
input: {
|
|
421
|
+
namespace: "okendo"
|
|
422
|
+
key: "WidgetPreRenderStyleTags"
|
|
423
|
+
ownerType: SHOP
|
|
424
|
+
}
|
|
425
|
+
) {
|
|
426
|
+
metafieldStorefrontVisibility {
|
|
427
|
+
id
|
|
428
|
+
}
|
|
429
|
+
userErrors {
|
|
430
|
+
field
|
|
431
|
+
message
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
2. Execute the following request to expose the `WidgetPreRenderBodyStyleTags` shop metafield:
|
|
438
|
+
|
|
439
|
+
```graphql
|
|
440
|
+
mutation {
|
|
441
|
+
metafieldStorefrontVisibilityCreate(
|
|
442
|
+
input: {
|
|
443
|
+
namespace: "okendo"
|
|
444
|
+
key: "WidgetPreRenderBodyStyleTags"
|
|
445
|
+
ownerType: SHOP
|
|
446
|
+
}
|
|
447
|
+
) {
|
|
448
|
+
metafieldStorefrontVisibility {
|
|
449
|
+
id
|
|
450
|
+
}
|
|
451
|
+
userErrors {
|
|
452
|
+
field
|
|
453
|
+
message
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
3. Execute the following request to expose the `ReviewsWidgetSnippet` product metafield:
|
|
460
|
+
|
|
461
|
+
```graphql
|
|
462
|
+
mutation {
|
|
463
|
+
metafieldStorefrontVisibilityCreate(
|
|
464
|
+
input: {
|
|
465
|
+
namespace: "okendo"
|
|
466
|
+
key: "ReviewsWidgetSnippet"
|
|
467
|
+
ownerType: PRODUCT
|
|
468
|
+
}
|
|
469
|
+
) {
|
|
470
|
+
metafieldStorefrontVisibility {
|
|
471
|
+
id
|
|
472
|
+
}
|
|
473
|
+
userErrors {
|
|
474
|
+
field
|
|
475
|
+
message
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
4. Execute the following request to expose the `StarRatingSnippet` the product metafield:
|
|
482
|
+
|
|
483
|
+
```graphql
|
|
484
|
+
mutation {
|
|
485
|
+
metafieldStorefrontVisibilityCreate(
|
|
486
|
+
input: { namespace: "okendo", key: "StarRatingSnippet", ownerType: PRODUCT }
|
|
487
|
+
) {
|
|
488
|
+
metafieldStorefrontVisibility {
|
|
489
|
+
id
|
|
490
|
+
}
|
|
491
|
+
userErrors {
|
|
492
|
+
field
|
|
493
|
+
message
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
</details>
|
|
500
|
+
|
|
501
|
+
</details>
|
|
502
|
+
|
|
503
|
+
## Installation
|
|
504
|
+
|
|
505
|
+
> The code examples provided in this section are based on the Shopify template store created by running `npm create @shopify/hydrogen@latest` (see [Shopify's documentation](https://shopify.dev/docs/custom-storefronts/hydrogen/getting-started)). You will find the following steps already done in [our demo store](https://github.com/okendo/okendo-shopify-hydrogen-demo).
|
|
506
|
+
|
|
507
|
+
Run:
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
npm i @okendo/shopify-hydrogen
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### `app/root.tsx`
|
|
514
|
+
|
|
515
|
+
Open `app/root.tsx` and add the following import:
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
import {
|
|
519
|
+
OkendoProvider,
|
|
520
|
+
getOkendoProviderData,
|
|
521
|
+
} from "@okendo/shopify-hydrogen";
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Locate the `loader` function, append `okendoProviderData` to the returned data as shown below, and set `subscriberId` to your Okendo subscriber ID:
|
|
525
|
+
|
|
526
|
+
```ts
|
|
527
|
+
return defer(
|
|
528
|
+
{
|
|
529
|
+
...
|
|
530
|
+
okendoProviderData: await getOkendoProviderData({
|
|
531
|
+
context,
|
|
532
|
+
subscriberId: "<your-okendo-subscriber-id>",
|
|
533
|
+
}),
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Locate the `App` function, add the `meta` tag `oke:subscriber_id` to `head`, and place your Okendo subscriber ID in its content:
|
|
539
|
+
|
|
540
|
+
```ts
|
|
541
|
+
<head>
|
|
542
|
+
<meta charSet="utf-8" />
|
|
543
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
544
|
+
<meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
|
|
545
|
+
...
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Append `OkendoProvider` to `body`, and pass it 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):
|
|
549
|
+
|
|
550
|
+
```tsx
|
|
551
|
+
...
|
|
552
|
+
<body>
|
|
553
|
+
<OkendoProvider
|
|
554
|
+
nonce={nonce}
|
|
555
|
+
okendoProviderData={data.okendoProviderData}
|
|
556
|
+
/>
|
|
557
|
+
...
|
|
558
|
+
</body>
|
|
559
|
+
...
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### `app/entry.server.tsx`
|
|
563
|
+
|
|
564
|
+
> This is only necessary if Content Security Policy is active in your project.
|
|
565
|
+
|
|
566
|
+
Locate the call to `createContentSecurityPolicy`, and add:
|
|
567
|
+
|
|
568
|
+
- `https://d3hw6dc1ow8pp2.cloudfront.net`, `https://d3g5hqndtiniji.cloudfront.net`, and `data:` to `defaultSrc`
|
|
569
|
+
- `https://d3hw6dc1ow8pp2.cloudfront.net` to `styleSrc`
|
|
570
|
+
- `https://api.okendo.io` to `connectSrc`
|
|
571
|
+
|
|
572
|
+
Note that it's necessary to to add the default values (`'self'`, etc.) when [extending the CSP](https://shopify.dev/docs/custom-storefronts/hydrogen/content-security-policy). The call to `createContentSecurityPolicy` should now look like the following:
|
|
573
|
+
|
|
574
|
+
```ts
|
|
575
|
+
const { nonce, header, NonceProvider } = createContentSecurityPolicy({
|
|
576
|
+
defaultSrc: [
|
|
577
|
+
"'self'",
|
|
578
|
+
"localhost:*",
|
|
579
|
+
"https://cdn.shopify.com",
|
|
580
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
581
|
+
"https://d3g5hqndtiniji.cloudfront.net",
|
|
582
|
+
"https://cdn-static.okendo.io",
|
|
583
|
+
"https://surveys.okendo.io",
|
|
584
|
+
"data:",
|
|
585
|
+
],
|
|
586
|
+
imgSrc: [
|
|
587
|
+
"'self'",
|
|
588
|
+
"https://cdn.shopify.com",
|
|
589
|
+
"data:",
|
|
590
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
591
|
+
"https://d3g5hqndtiniji.cloudfront.net",
|
|
592
|
+
"https://cdn-static.okendo.io",
|
|
593
|
+
"https://surveys.okendo.io",
|
|
594
|
+
],
|
|
595
|
+
mediaSrc: [
|
|
596
|
+
"'self'",
|
|
597
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
598
|
+
"https://d3g5hqndtiniji.cloudfront.net",
|
|
599
|
+
"https://cdn-static.okendo.io",
|
|
600
|
+
],
|
|
601
|
+
styleSrcElem: [
|
|
602
|
+
"'self'",
|
|
603
|
+
"'unsafe-inline'",
|
|
604
|
+
"https://cdn.shopify.com",
|
|
605
|
+
"https://fonts.googleapis.com",
|
|
606
|
+
"https://fonts.gstatic.com",
|
|
607
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
608
|
+
"https://cdn-static.okendo.io",
|
|
609
|
+
"https://surveys.okendo.io",
|
|
610
|
+
],
|
|
611
|
+
scriptSrc: [
|
|
612
|
+
"'self'",
|
|
613
|
+
"https://cdn.shopify.com",
|
|
614
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
615
|
+
"https://cdn-static.okendo.io",
|
|
616
|
+
"https://surveys.okendo.io",
|
|
617
|
+
],
|
|
618
|
+
fontSrc: [
|
|
619
|
+
"'self'",
|
|
620
|
+
"https://fonts.gstatic.com",
|
|
621
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
622
|
+
"https://cdn.shopify.com",
|
|
623
|
+
"https://cdn-static.okendo.io",
|
|
624
|
+
"https://surveys.okendo.io",
|
|
625
|
+
],
|
|
626
|
+
connectSrc: [
|
|
627
|
+
"'self'",
|
|
628
|
+
"https://monorail-edge.shopifysvc.com",
|
|
629
|
+
"localhost:*",
|
|
630
|
+
"ws://localhost:*",
|
|
631
|
+
"ws://127.0.0.1:*",
|
|
632
|
+
"https://api.okendo.io",
|
|
633
|
+
"https://cdn-static.okendo.io",
|
|
634
|
+
"https://surveys.okendo.io",
|
|
635
|
+
],
|
|
636
|
+
});
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### `app/routes/_index.tsx`
|
|
640
|
+
|
|
641
|
+
Add the following imports:
|
|
642
|
+
|
|
643
|
+
```ts
|
|
644
|
+
import {
|
|
645
|
+
OkendoStarRating,
|
|
646
|
+
type WithOkendoStarRatingSnippet,
|
|
647
|
+
} from "@okendo/shopify-hydrogen";
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
Add the following block just before the `RECOMMENDED_PRODUCTS_QUERY` GraphQL query:
|
|
651
|
+
|
|
652
|
+
```ts
|
|
653
|
+
const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
|
|
654
|
+
fragment OkendoStarRatingSnippet on Product {
|
|
655
|
+
okendoStarRatingSnippet: metafield(
|
|
656
|
+
namespace: "okendo"
|
|
657
|
+
key: "StarRatingSnippet"
|
|
658
|
+
) {
|
|
659
|
+
value
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
` as const;
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
Then append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}` and `...OkendoStarRatingSnippet` to `RECOMMENDED_PRODUCTS_QUERY`:
|
|
666
|
+
|
|
667
|
+
```ts
|
|
668
|
+
const RECOMMENDED_PRODUCTS_QUERY = `#graphql
|
|
669
|
+
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
|
|
670
|
+
fragment RecommendedProduct on Product {
|
|
671
|
+
id
|
|
672
|
+
title
|
|
673
|
+
handle
|
|
674
|
+
priceRange {
|
|
675
|
+
minVariantPrice {
|
|
676
|
+
amount
|
|
677
|
+
currencyCode
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
images(first: 1) {
|
|
681
|
+
nodes {
|
|
682
|
+
id
|
|
683
|
+
url
|
|
684
|
+
altText
|
|
685
|
+
width
|
|
686
|
+
height
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
...OkendoStarRatingSnippet
|
|
690
|
+
}
|
|
691
|
+
query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
|
|
692
|
+
@inContext(country: $country, language: $language) {
|
|
693
|
+
products(first: 4, sortKey: UPDATED_AT, reverse: true) {
|
|
694
|
+
nodes {
|
|
695
|
+
...RecommendedProduct
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
` as const;
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Tweak the type of the `products` prop of `RecommendedProducts`:
|
|
703
|
+
|
|
704
|
+
```ts
|
|
705
|
+
products: Promise<{
|
|
706
|
+
products: {
|
|
707
|
+
nodes: (RecommendedProductsQuery["products"]["nodes"][0] &
|
|
708
|
+
WithOkendoStarRatingSnippet)[];
|
|
709
|
+
};
|
|
710
|
+
}>;
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
Add `OkendoStarRating` to `RecommendedProducts`:
|
|
714
|
+
|
|
715
|
+
```tsx
|
|
716
|
+
<OkendoStarRating
|
|
717
|
+
productId={product.id}
|
|
718
|
+
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
719
|
+
/>
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
For instance, we can add it below the product title, like this:
|
|
723
|
+
|
|
724
|
+
```tsx
|
|
725
|
+
<Image
|
|
726
|
+
data={product.images.nodes[0]}
|
|
727
|
+
aspectRatio="1/1"
|
|
728
|
+
sizes="(min-width: 45em) 20vw, 50vw"
|
|
729
|
+
/>
|
|
730
|
+
<h4>{product.title}</h4>
|
|
731
|
+
<OkendoStarRating
|
|
732
|
+
productId={product.id}
|
|
733
|
+
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
734
|
+
/>
|
|
735
|
+
<small>
|
|
736
|
+
<Money data={product.priceRange.minVariantPrice} />
|
|
737
|
+
</small>
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
We now have the Okendo Star Rating widget visible on our page:
|
|
741
|
+
|
|
742
|
+

|
|
743
|
+
|
|
744
|
+
### `app/routes/products.$handle.tsx`
|
|
745
|
+
|
|
746
|
+
Add the following imports:
|
|
747
|
+
|
|
748
|
+
```ts
|
|
749
|
+
import {
|
|
750
|
+
OKENDO_PRODUCT_REVIEWS_FRAGMENT,
|
|
751
|
+
OKENDO_PRODUCT_STAR_RATING_FRAGMENT,
|
|
752
|
+
OkendoReviews,
|
|
753
|
+
OkendoStarRating,
|
|
754
|
+
type WithOkendoReviewsSnippet,
|
|
755
|
+
type WithOkendoStarRatingSnippet,
|
|
756
|
+
} from "@okendo/shopify-hydrogen";
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
Add the following block just before the `RECOMMENDED_PRODUCTS_QUERY` GraphQL query:
|
|
760
|
+
|
|
761
|
+
```ts
|
|
762
|
+
const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
|
|
763
|
+
fragment OkendoStarRatingSnippet on Product {
|
|
764
|
+
okendoStarRatingSnippet: metafield(
|
|
765
|
+
namespace: "okendo"
|
|
766
|
+
key: "StarRatingSnippet"
|
|
767
|
+
) {
|
|
768
|
+
value
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
` as const;
|
|
772
|
+
|
|
773
|
+
const OKENDO_PRODUCT_REVIEWS_FRAGMENT = `#graphql
|
|
774
|
+
fragment OkendoReviewsSnippet on Product {
|
|
775
|
+
okendoReviewsSnippet: metafield(
|
|
776
|
+
namespace: "okendo"
|
|
777
|
+
key: "ReviewsWidgetSnippet"
|
|
778
|
+
) {
|
|
779
|
+
value
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
` as const;
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
Then append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}`, `${OKENDO_PRODUCT_REVIEWS_FRAGMENT}`, `...OkendoStarRatingSnippet`, and `...OkendoReviewsSnippet` to `PRODUCT_FRAGMENT`:
|
|
786
|
+
|
|
787
|
+
```ts
|
|
788
|
+
const PRODUCT_FRAGMENT = `#graphql
|
|
789
|
+
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
|
|
790
|
+
${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
|
|
791
|
+
fragment Product on Product {
|
|
792
|
+
id
|
|
793
|
+
title
|
|
794
|
+
vendor
|
|
795
|
+
handle
|
|
796
|
+
descriptionHtml
|
|
797
|
+
description
|
|
798
|
+
options {
|
|
799
|
+
name
|
|
800
|
+
values
|
|
801
|
+
}
|
|
802
|
+
selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
|
|
803
|
+
...ProductVariant
|
|
804
|
+
}
|
|
805
|
+
variants(first: 1) {
|
|
806
|
+
nodes {
|
|
807
|
+
...ProductVariant
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
seo {
|
|
811
|
+
description
|
|
812
|
+
title
|
|
813
|
+
}
|
|
814
|
+
...OkendoStarRatingSnippet
|
|
815
|
+
...OkendoReviewsSnippet
|
|
816
|
+
}
|
|
817
|
+
${PRODUCT_VARIANT_FRAGMENT}
|
|
818
|
+
` as const;
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
Add `OkendoReviews` to `Product`:
|
|
822
|
+
|
|
823
|
+
```tsx
|
|
824
|
+
<OkendoReviews
|
|
825
|
+
productId={product.id}
|
|
826
|
+
okendoReviewsSnippet={product.okendoReviewsSnippet}
|
|
827
|
+
/>
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
For instance, we can add it below the product section, like this:
|
|
831
|
+
|
|
832
|
+
```tsx
|
|
833
|
+
<>
|
|
834
|
+
<div className="product">
|
|
835
|
+
<ProductImage image={selectedVariant?.image} />
|
|
836
|
+
<ProductMain
|
|
837
|
+
selectedVariant={selectedVariant}
|
|
838
|
+
product={product}
|
|
839
|
+
variants={variants}
|
|
840
|
+
/>
|
|
841
|
+
</div>
|
|
842
|
+
|
|
843
|
+
<OkendoReviews
|
|
844
|
+
productId={product.id}
|
|
845
|
+
okendoReviewsSnippet={product.okendoReviewsSnippet}
|
|
846
|
+
/>
|
|
847
|
+
</>
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
Tweak the type of the `product` prop of `ProductMain`:
|
|
851
|
+
|
|
852
|
+
```ts
|
|
853
|
+
product: ProductFragment &
|
|
854
|
+
WithOkendoStarRatingSnippet &
|
|
855
|
+
WithOkendoReviewsSnippet;
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
Add `OkendoStarRating` to `ProductMain`:
|
|
859
|
+
|
|
860
|
+
```tsx
|
|
861
|
+
<OkendoStarRating
|
|
862
|
+
productId={product.id}
|
|
863
|
+
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
864
|
+
/>
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
For instance, we can add it below the product title, like this:
|
|
868
|
+
|
|
869
|
+
```tsx
|
|
870
|
+
<div className="product-main">
|
|
871
|
+
<h1>{title}</h1>
|
|
872
|
+
<OkendoStarRating
|
|
873
|
+
productId={product.id}
|
|
874
|
+
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
875
|
+
/>
|
|
876
|
+
<ProductPrice selectedVariant={selectedVariant} />
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
We now have the Okendo Star Rating and Reviews widgets visible on our product page:
|
|
880
|
+
|
|
881
|
+

|