@okendo/shopify-hydrogen 2.0.2 → 2.1.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 +188 -173
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/types/components/OkendoProvider/OkendoProvider.d.ts +13 -0
- package/dist/cjs/types/components/OkendoReviews/OkendoReviews.d.ts +1 -1
- package/dist/cjs/types/components/OkendoStarRating/OkendoStarRating.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/types/components/OkendoProvider/OkendoProvider.d.ts +13 -0
- package/dist/esm/types/components/OkendoReviews/OkendoReviews.d.ts +1 -1
- package/dist/esm/types/components/OkendoStarRating/OkendoStarRating.d.ts +1 -1
- package/dist/index.d.ts +16 -3
- package/package.json +20 -19
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ This package brings [Okendo's review widgets](https://www.okendo.io/blog/widget-
|
|
|
6
6
|
|
|
7
7
|
## Requirements
|
|
8
8
|
|
|
9
|
-
- A Shopify store with the **Okendo: Product Reviews & UCG** app installed and configured.
|
|
9
|
+
- A Shopify store with the [**Okendo: Product Reviews & UCG**](https://apps.shopify.com/okendo-reviews) app installed and configured.
|
|
10
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
11
|
- A current Okendo subscription.
|
|
12
12
|
- A [Storefront access token](https://github.com/okendo/okendo-shopify-hydrogen-demo/wiki/Configure-Shopify-Storefront-API).
|
|
@@ -16,6 +16,8 @@ This package brings [Okendo's review widgets](https://www.okendo.io/blog/widget-
|
|
|
16
16
|
|
|
17
17
|
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).
|
|
18
18
|
|
|
19
|
+
> Note: there have been multiple versions of Shopify's Hydrogen demo store. If you project is based on an old version of it, consult our version history to find out how to add Okendo to it.
|
|
20
|
+
|
|
19
21
|
## How it works
|
|
20
22
|
|
|
21
23
|
This package provides:
|
|
@@ -25,7 +27,7 @@ This package provides:
|
|
|
25
27
|
|
|
26
28
|
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`.
|
|
27
29
|
|
|
28
|
-
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
|
+
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.
|
|
29
31
|
|
|
30
32
|
## Expose Shopify Metafields <a id="expose-shopify-metafields" name="expose-shopify-metafields"></a>
|
|
31
33
|
|
|
@@ -254,7 +256,7 @@ mutation {
|
|
|
254
256
|
|
|
255
257
|
## Installation
|
|
256
258
|
|
|
257
|
-
> The code examples provided in this section are based on the [demo store provided by Shopify](https://github.com/Shopify/hydrogen/tree/
|
|
259
|
+
> The code examples provided in this section are based on the [demo store provided by Shopify](https://github.com/Shopify/hydrogen/tree/main/templates/demo-store). You will find the following steps already done in [our demo store](https://github.com/okendo/okendo-shopify-hydrogen-demo).
|
|
258
260
|
|
|
259
261
|
Run:
|
|
260
262
|
|
|
@@ -273,44 +275,11 @@ import {
|
|
|
273
275
|
} from "@okendo/shopify-hydrogen";
|
|
274
276
|
```
|
|
275
277
|
|
|
276
|
-
Locate the `
|
|
277
|
-
|
|
278
|
-
```ts
|
|
279
|
-
'oke:subscriber_id': '<your-okendo-subscriber-id>',
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
The `meta` function should now look like the following:
|
|
283
|
-
|
|
284
|
-
```ts
|
|
285
|
-
export const meta: MetaFunction = () => ({
|
|
286
|
-
charset: "utf-8",
|
|
287
|
-
viewport: "width=device-width,initial-scale=1",
|
|
288
|
-
"oke:subscriber_id": "<your-okendo-subscriber-id>",
|
|
289
|
-
});
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
Locate the `loader` function and append the `okendoProviderData` property to the returned data:
|
|
293
|
-
|
|
294
|
-
```ts
|
|
295
|
-
okendoProviderData: await getOkendoProviderData({
|
|
296
|
-
context,
|
|
297
|
-
subscriberId: '<your-okendo-subscriber-id>',
|
|
298
|
-
}),
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
The `loader` function returned data should now look like the following:
|
|
278
|
+
Locate the `loader` function, append the `okendoProviderData` property to the returned data, and set `subscriberId` to your Okendo subscriber ID:
|
|
302
279
|
|
|
303
280
|
```ts
|
|
304
281
|
return defer({
|
|
305
|
-
|
|
306
|
-
layout,
|
|
307
|
-
selectedLocale: context.storefront.i18n,
|
|
308
|
-
cart: cartId ? getCart(context, cartId) : undefined,
|
|
309
|
-
analytics: {
|
|
310
|
-
shopifySalesChannel: ShopifySalesChannel.hydrogen,
|
|
311
|
-
shopId: layout.shop.id,
|
|
312
|
-
},
|
|
313
|
-
seo,
|
|
282
|
+
...
|
|
314
283
|
okendoProviderData: await getOkendoProviderData({
|
|
315
284
|
context,
|
|
316
285
|
subscriberId: "<your-okendo-subscriber-id>",
|
|
@@ -318,78 +287,131 @@ return defer({
|
|
|
318
287
|
});
|
|
319
288
|
```
|
|
320
289
|
|
|
321
|
-
Locate the `App` function,
|
|
290
|
+
Locate the `App` function, add the `meta` tag `oke:subscriber_id` to `head`, and place your Okendo subscriber ID in its content:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
<head>
|
|
294
|
+
<meta charSet="utf-8" />
|
|
295
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
296
|
+
<meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
|
|
297
|
+
...
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
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):
|
|
322
301
|
|
|
323
302
|
```tsx
|
|
324
303
|
...
|
|
325
304
|
<body>
|
|
326
|
-
<OkendoProvider
|
|
305
|
+
<OkendoProvider
|
|
306
|
+
nonce={nonce}
|
|
307
|
+
okendoProviderData={data.okendoProviderData}
|
|
308
|
+
/>
|
|
327
309
|
...
|
|
328
310
|
</body>
|
|
329
311
|
...
|
|
330
312
|
```
|
|
331
313
|
|
|
332
|
-
### `app/
|
|
314
|
+
### `app/entry.server.tsx`
|
|
333
315
|
|
|
334
|
-
|
|
316
|
+
> This is only necessary if Content Security Policy is active in your project.
|
|
335
317
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
318
|
+
Locate the call to `createContentSecurityPolicy`, and add:
|
|
319
|
+
|
|
320
|
+
- `https://d3hw6dc1ow8pp2.cloudfront.net`, `https://d3g5hqndtiniji.cloudfront.net`, and `data:` to `defaultSrc`
|
|
321
|
+
- `https://d3hw6dc1ow8pp2.cloudfront.net` to `styleSrc`
|
|
322
|
+
- `https://api.okendo.io` to `connectSrc`
|
|
339
323
|
|
|
340
|
-
|
|
324
|
+
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:
|
|
341
325
|
|
|
342
326
|
```ts
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
327
|
+
const { nonce, header, NonceProvider } = createContentSecurityPolicy({
|
|
328
|
+
defaultSrc: [
|
|
329
|
+
"'self'",
|
|
330
|
+
"localhost:*",
|
|
331
|
+
"https://cdn.shopify.com",
|
|
332
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
333
|
+
"https://d3g5hqndtiniji.cloudfront.net",
|
|
334
|
+
"data:",
|
|
335
|
+
],
|
|
336
|
+
styleSrc: [
|
|
337
|
+
"'self'",
|
|
338
|
+
"'unsafe-inline'",
|
|
339
|
+
"https://cdn.shopify.com",
|
|
340
|
+
"localhost:*",
|
|
341
|
+
"https://d3hw6dc1ow8pp2.cloudfront.net",
|
|
342
|
+
],
|
|
343
|
+
connectSrc: [
|
|
344
|
+
"'self'",
|
|
345
|
+
"https://monorail-edge.shopifysvc.com",
|
|
346
|
+
"localhost:*",
|
|
347
|
+
"ws://localhost:*",
|
|
348
|
+
"ws://127.0.0.1:*",
|
|
349
|
+
"https://api.okendo.io",
|
|
350
|
+
],
|
|
351
|
+
});
|
|
353
352
|
```
|
|
354
353
|
|
|
355
|
-
### `app/
|
|
354
|
+
### `app/routes/_index.tsx`
|
|
356
355
|
|
|
357
|
-
|
|
356
|
+
Add the following imports:
|
|
358
357
|
|
|
359
358
|
```ts
|
|
360
359
|
import {
|
|
360
|
+
OKENDO_PRODUCT_STAR_RATING_FRAGMENT,
|
|
361
361
|
OkendoStarRating,
|
|
362
362
|
WithOkendoStarRatingSnippet,
|
|
363
363
|
} from "@okendo/shopify-hydrogen";
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
-
|
|
366
|
+
Locate the `RECOMMENDED_PRODUCTS_QUERY` GraphQL query, and append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}` `...OkendoStarRatingSnippet` to it:
|
|
367
367
|
|
|
368
368
|
```ts
|
|
369
|
-
|
|
369
|
+
const RECOMMENDED_PRODUCTS_QUERY = `#graphql
|
|
370
|
+
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
|
|
371
|
+
fragment RecommendedProduct on Product {
|
|
372
|
+
id
|
|
373
|
+
title
|
|
374
|
+
handle
|
|
375
|
+
priceRange {
|
|
376
|
+
minVariantPrice {
|
|
377
|
+
amount
|
|
378
|
+
currencyCode
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
images(first: 1) {
|
|
382
|
+
nodes {
|
|
383
|
+
id
|
|
384
|
+
url
|
|
385
|
+
altText
|
|
386
|
+
width
|
|
387
|
+
height
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
...OkendoStarRatingSnippet
|
|
391
|
+
}
|
|
392
|
+
query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
|
|
393
|
+
@inContext(country: $country, language: $language) {
|
|
394
|
+
products(first: 4, sortKey: UPDATED_AT, reverse: true) {
|
|
395
|
+
nodes {
|
|
396
|
+
...RecommendedProduct
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
` as const;
|
|
370
401
|
```
|
|
371
402
|
|
|
372
|
-
`
|
|
403
|
+
Tweak the type of the `products` prop of `RecommendedProducts`:
|
|
373
404
|
|
|
374
405
|
```ts
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
quickAdd,
|
|
382
|
-
}: {
|
|
383
|
-
product: SerializeFrom<Product & WithOkendoStarRatingSnippet>;
|
|
384
|
-
label?: string;
|
|
385
|
-
className?: string;
|
|
386
|
-
loading?: HTMLImageElement['loading'];
|
|
387
|
-
onClick?: () => void;
|
|
388
|
-
quickAdd?: boolean;
|
|
389
|
-
}) {
|
|
406
|
+
products: Promise<{
|
|
407
|
+
products: {
|
|
408
|
+
nodes: (RecommendedProductsQuery['products']['nodes'][0] &
|
|
409
|
+
WithOkendoStarRatingSnippet)[];
|
|
410
|
+
};
|
|
411
|
+
};
|
|
390
412
|
```
|
|
391
413
|
|
|
392
|
-
Add
|
|
414
|
+
Add `OkendoStarRating` to `RecommendedProducts`:
|
|
393
415
|
|
|
394
416
|
```tsx
|
|
395
417
|
<OkendoStarRating
|
|
@@ -398,144 +420,137 @@ Add the `OkendoStarRating` component:
|
|
|
398
420
|
/>
|
|
399
421
|
```
|
|
400
422
|
|
|
401
|
-
For instance,
|
|
423
|
+
For instance, we can add it below the product title, like this:
|
|
402
424
|
|
|
403
425
|
```tsx
|
|
404
|
-
<
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
</
|
|
426
|
+
<Image
|
|
427
|
+
data={product.images.nodes[0]}
|
|
428
|
+
aspectRatio="1/1"
|
|
429
|
+
sizes="(min-width: 45em) 20vw, 50vw"
|
|
430
|
+
/>
|
|
431
|
+
<h4>{product.title}</h4>
|
|
410
432
|
<OkendoStarRating
|
|
411
433
|
productId={product.id}
|
|
412
434
|
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
413
435
|
/>
|
|
436
|
+
<small>
|
|
437
|
+
<Money data={product.priceRange.minVariantPrice} />
|
|
438
|
+
</small>
|
|
414
439
|
```
|
|
415
440
|
|
|
416
|
-
|
|
441
|
+
We now have the Okendo Star Rating widget visible on our page:
|
|
442
|
+
|
|
443
|
+

|
|
444
|
+
|
|
445
|
+
### `app/routes/products.$handle.tsx`
|
|
417
446
|
|
|
418
|
-
|
|
447
|
+
Add the following imports:
|
|
419
448
|
|
|
420
449
|
```ts
|
|
421
450
|
import {
|
|
451
|
+
OKENDO_PRODUCT_REVIEWS_FRAGMENT,
|
|
452
|
+
OKENDO_PRODUCT_STAR_RATING_FRAGMENT,
|
|
422
453
|
OkendoReviews,
|
|
423
454
|
OkendoStarRating,
|
|
424
|
-
WithOkendoStarRatingSnippet,
|
|
425
455
|
WithOkendoReviewsSnippet,
|
|
426
|
-
|
|
427
|
-
OKENDO_PRODUCT_REVIEWS_FRAGMENT,
|
|
456
|
+
WithOkendoStarRatingSnippet,
|
|
428
457
|
} from "@okendo/shopify-hydrogen";
|
|
429
458
|
```
|
|
430
459
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
```ts
|
|
434
|
-
product: ProductType & {
|
|
435
|
-
selectedVariant?: ProductVariant;
|
|
436
|
-
} & WithOkendoStarRatingSnippet &
|
|
437
|
-
WithOkendoReviewsSnippet;
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
The storefront query should now look like this:
|
|
460
|
+
Locate the `PRODUCT_FRAGMENT` GraphQL query, and append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}`, `${OKENDO_PRODUCT_REVIEWS_FRAGMENT}`, `...OkendoStarRatingSnippet`, and `...OkendoReviewsSnippet` to it:
|
|
441
461
|
|
|
442
462
|
```ts
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
463
|
+
const PRODUCT_FRAGMENT = `#graphql
|
|
464
|
+
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
|
|
465
|
+
${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
|
|
466
|
+
fragment Product on Product {
|
|
467
|
+
id
|
|
468
|
+
title
|
|
469
|
+
vendor
|
|
470
|
+
handle
|
|
471
|
+
descriptionHtml
|
|
472
|
+
description
|
|
473
|
+
options {
|
|
474
|
+
name
|
|
475
|
+
values
|
|
476
|
+
}
|
|
477
|
+
selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
|
|
478
|
+
...ProductVariant
|
|
479
|
+
}
|
|
480
|
+
variants(first: 1) {
|
|
481
|
+
nodes {
|
|
482
|
+
...ProductVariant
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
seo {
|
|
486
|
+
description
|
|
487
|
+
title
|
|
488
|
+
}
|
|
489
|
+
...OkendoStarRatingSnippet
|
|
490
|
+
...OkendoReviewsSnippet
|
|
491
|
+
}
|
|
492
|
+
${PRODUCT_VARIANT_FRAGMENT}
|
|
493
|
+
` as const;
|
|
457
494
|
```
|
|
458
495
|
|
|
459
|
-
Add
|
|
496
|
+
Add `OkendoReviews` to `Product`:
|
|
460
497
|
|
|
461
498
|
```tsx
|
|
462
|
-
<
|
|
499
|
+
<OkendoReviews
|
|
463
500
|
productId={product.id}
|
|
464
|
-
|
|
501
|
+
okendoReviewsSnippet={product.okendoReviewsSnippet}
|
|
465
502
|
/>
|
|
466
503
|
```
|
|
467
504
|
|
|
468
|
-
For instance,
|
|
505
|
+
For instance, we can add it below the product section, like this:
|
|
469
506
|
|
|
470
507
|
```tsx
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
<
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
508
|
+
<>
|
|
509
|
+
<div className="product">
|
|
510
|
+
<ProductImage image={selectedVariant?.image} />
|
|
511
|
+
<ProductMain
|
|
512
|
+
selectedVariant={selectedVariant}
|
|
513
|
+
product={product}
|
|
514
|
+
variants={variants}
|
|
515
|
+
/>
|
|
516
|
+
</div>
|
|
517
|
+
|
|
518
|
+
<OkendoReviews
|
|
519
|
+
productId={product.id}
|
|
520
|
+
okendoReviewsSnippet={product.okendoReviewsSnippet}
|
|
521
|
+
/>
|
|
522
|
+
</>
|
|
481
523
|
```
|
|
482
524
|
|
|
483
|
-
|
|
525
|
+
Tweak the type of the `product` prop of `ProductMain`:
|
|
484
526
|
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
/>
|
|
527
|
+
```ts
|
|
528
|
+
product: ProductFragment &
|
|
529
|
+
WithOkendoStarRatingSnippet &
|
|
530
|
+
WithOkendoReviewsSnippet;
|
|
490
531
|
```
|
|
491
532
|
|
|
492
|
-
|
|
533
|
+
Add `OkendoStarRating` to `ProductMain`:
|
|
493
534
|
|
|
494
535
|
```tsx
|
|
495
|
-
|
|
496
|
-
</Section>
|
|
497
|
-
<OkendoReviews
|
|
536
|
+
<OkendoStarRating
|
|
498
537
|
productId={product.id}
|
|
499
|
-
|
|
538
|
+
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
500
539
|
/>
|
|
501
|
-
<Suspense fallback={<Skeleton className="h-32" />}>
|
|
502
|
-
...
|
|
503
540
|
```
|
|
504
541
|
|
|
505
|
-
|
|
542
|
+
For instance, we can add it below the product title, like this:
|
|
506
543
|
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
544
|
+
```tsx
|
|
545
|
+
<div className="product-main">
|
|
546
|
+
<h1>{title}</h1>
|
|
547
|
+
<OkendoStarRating
|
|
548
|
+
productId={product.id}
|
|
549
|
+
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
550
|
+
/>
|
|
551
|
+
<ProductPrice selectedVariant={selectedVariant} />
|
|
514
552
|
```
|
|
515
553
|
|
|
516
|
-
|
|
554
|
+
We now have the Okendo Star Rating and Reviews widgets visible on our product page:
|
|
517
555
|
|
|
518
|
-
|
|
519
|
-
const PRODUCT_QUERY = `#graphql
|
|
520
|
-
${MEDIA_FRAGMENT}
|
|
521
|
-
${PRODUCT_VARIANT_FRAGMENT}
|
|
522
|
-
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
|
|
523
|
-
${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
|
|
524
|
-
query Product(
|
|
525
|
-
$country: CountryCode
|
|
526
|
-
$language: LanguageCode
|
|
527
|
-
$handle: String!
|
|
528
|
-
$selectedOptions: [SelectedOptionInput!]!
|
|
529
|
-
) @inContext(country: $country, language: $language) {
|
|
530
|
-
product(handle: $handle) {
|
|
531
|
-
id
|
|
532
|
-
title
|
|
533
|
-
vendor
|
|
534
|
-
handle
|
|
535
|
-
descriptionHtml
|
|
536
|
-
description
|
|
537
|
-
...OkendoStarRatingSnippet
|
|
538
|
-
...OkendoReviewsSnippet
|
|
539
|
-
options {
|
|
540
|
-
...
|
|
541
|
-
```
|
|
556
|
+

|
package/dist/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("react");const e=({dataAttributes:e,metafieldContent:n=""})=>{const r=t.useRef(null),
|
|
1
|
+
"use strict";var t=require("react");const e=({dataAttributes:e,metafieldContent:n=""})=>{const r=t.useRef(null),o=()=>{r.current&&window.okeWidgetApi.initWidget(r.current)};return t.useEffect((()=>(window.okeWidgetApi&&r.current?o():document.addEventListener("oke-script-loaded",o),()=>{document.removeEventListener("oke-script-loaded",o)})),[e]),t.createElement("div",{ref:r,key:JSON.stringify(e),...e,dangerouslySetInnerHTML:n?{__html:n}:void 0})},n=/^[0-9]*$/;function r(t){if(t)return`shopify-${n.test(t)?t:t.split("/").slice(-1)[0]}`}exports.OKENDO_PRODUCT_REVIEWS_FRAGMENT='#graphql\n\tfragment OkendoReviewsSnippet on Product {\n\t\tokendoReviewsSnippet: metafield(\n\t\t\tnamespace: "okendo"\n\t\t\tkey: "ReviewsWidgetSnippet"\n\t\t) {\n\t\t\tvalue\n\t\t}\n\t}\n',exports.OKENDO_PRODUCT_STAR_RATING_FRAGMENT='#graphql\n\tfragment OkendoStarRatingSnippet on Product {\n\t\tokendoStarRatingSnippet: metafield(\n\t\t\tnamespace: "okendo"\n\t\t\tkey: "StarRatingSnippet"\n\t\t) {\n\t\t\tvalue\n\t\t}\n\t}\n',exports.OkendoProvider=({nonce:e,okendoProviderData:n,productUrlFormatter:r})=>{if(!n)return null;const{reviewsHeaderConfig:o,cssVariables:s,customCss:i,initScriptContents:a,preRenderStyleTags:d,starSymbols:c}=n,l="function"==typeof r?r.toString():"string"==typeof r?r:"(product) =>\n\t\t\t\t\tproduct && product.productHandle\n\t\t\t\t\t\t? \"/products/\" + product.productHandle + \"/\" + (product.variantId ? '?variantId=' + product.variantId : '')\n\t\t\t\t\t\t: undefined",p=(s??"").replace('<style id="oke-css-vars">',"").replace("</style>",""),u=i?i.replace('<style id="oke-reviews-custom-css">',"").replace("</style>",""):"";return t.createElement(t.Fragment,null,t.createElement("script",{nonce:e||"",id:"oke-reviews-settings",type:"application/json",dangerouslySetInnerHTML:{__html:JSON.stringify(o)}}),t.createElement("style",{nonce:e||"",id:"oke-css-vars",dangerouslySetInnerHTML:{__html:p}}),u&&t.createElement("style",{nonce:e||"",id:"oke-reviews-custom-css",dangerouslySetInnerHTML:{__html:u}}),a&&t.createElement("script",{nonce:e||"",dangerouslySetInnerHTML:{__html:a}}),t.createElement("script",{nonce:e||"",type:"text/javascript",dangerouslySetInnerHTML:{__html:`window.okeProductUrlFormatter = ${l.toString()}`}}),d&&t.createElement("div",{dangerouslySetInnerHTML:{__html:d}}),c&&t.createElement("div",{dangerouslySetInnerHTML:{__html:c}}))},exports.OkendoReviews=({productId:n,okendoReviewsSnippet:o})=>{const s={"data-oke-widget":"","data-oke-reviews-product-id":r(n)};return t.createElement(e,{dataAttributes:s,metafieldContent:o?.value})},exports.OkendoStarRating=({productId:n,okendoStarRatingSnippet:o})=>{const s={"data-oke-star-rating":"","data-oke-reviews-product-id":r(n)};return t.createElement(e,{dataAttributes:s,metafieldContent:o?.value})},exports.getOkendoProviderData=async({context:t,subscriberId:e,apiDomain:n,cdnDomain:r})=>{const o=`https://${n||"api.okendo.io/v1"}/stores/${e}/widget_plus_settings`,s=await fetch(o);if(!s.ok)return console.error(`Failed to retrieve subscriber settings for subscriber ID '${e}'.`),null;const{reviewsHeaderConfig:i,cssVariables:a,customCss:d,starSymbols:c}=await s.json(),l=await fetch(`https://${r||"cdn-static.okendo.io"}/reviews-widget-plus/js/okendo-reviews.js`);if(!l.ok)return console.error("Failed to retrieve widget initialisation script."),null;const p=await l.text(),{shop:{widgetPreRenderStyleTags:u}}=await t.storefront.query('#graphql\n\t\tquery metafields {\n\t\t\tshop {\n\t\t\t\twidgetPreRenderStyleTags: metafield(\n\t\t\t\t\tnamespace: "okendo"\n\t\t\t\t\tkey: "WidgetPreRenderStyleTags"\n\t\t\t\t) {\n\t\t\t\t\tvalue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t'),g=u?.value??"";return g||console.warn("Failed to retrieve pre-rendered widget style settings."),{reviewsHeaderConfig:i,cssVariables:a,customCss:d,initScriptContents:p,preRenderStyleTags:g,starSymbols:c}};
|
|
@@ -23,8 +23,21 @@ export declare const getOkendoProviderData: ({ context, subscriberId, apiDomain,
|
|
|
23
23
|
preRenderStyleTags: string;
|
|
24
24
|
starSymbols: string;
|
|
25
25
|
} | null>;
|
|
26
|
+
interface ReviewProduct {
|
|
27
|
+
productHandle?: string;
|
|
28
|
+
productId: string;
|
|
29
|
+
variantId?: string;
|
|
30
|
+
}
|
|
26
31
|
interface OkendoProviderProps {
|
|
32
|
+
/**
|
|
33
|
+
* A nonce that will be added to Okendo's scripts;
|
|
34
|
+
* this is required if Content Security Policy is set up on your store.
|
|
35
|
+
*/
|
|
36
|
+
nonce?: string;
|
|
37
|
+
/** The data returned by `getOkendoProviderData` */
|
|
27
38
|
okendoProviderData: Partial<OkendoProviderData> | null;
|
|
39
|
+
/** An optional product URL formatter */
|
|
40
|
+
productUrlFormatter?: (product: ReviewProduct) => string;
|
|
28
41
|
}
|
|
29
42
|
export declare const OkendoProvider: FC<OkendoProviderProps>;
|
|
30
43
|
export {};
|
|
@@ -5,7 +5,7 @@ export interface WithOkendoStarRatingSnippet {
|
|
|
5
5
|
}
|
|
6
6
|
interface OkendoStarRatingProps {
|
|
7
7
|
productId: string;
|
|
8
|
-
okendoStarRatingSnippet
|
|
8
|
+
okendoStarRatingSnippet?: MetafieldValue;
|
|
9
9
|
}
|
|
10
10
|
export declare const OkendoStarRating: FC<OkendoStarRatingProps>;
|
|
11
11
|
export {};
|
package/dist/esm/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import t,{useRef as e,useEffect as n}from"react";const r=async({context:t,subscriberId:e,apiDomain:n,cdnDomain:r})=>{const
|
|
1
|
+
import t,{useRef as e,useEffect as n}from"react";const r=async({context:t,subscriberId:e,apiDomain:n,cdnDomain:r})=>{const o=`https://${n||"api.okendo.io/v1"}/stores/${e}/widget_plus_settings`,i=await fetch(o);if(!i.ok)return console.error(`Failed to retrieve subscriber settings for subscriber ID '${e}'.`),null;const{reviewsHeaderConfig:s,cssVariables:a,customCss:d,starSymbols:c}=await i.json(),l=await fetch(`https://${r||"cdn-static.okendo.io"}/reviews-widget-plus/js/okendo-reviews.js`);if(!l.ok)return console.error("Failed to retrieve widget initialisation script."),null;const p=await l.text(),{shop:{widgetPreRenderStyleTags:u}}=await t.storefront.query('#graphql\n\t\tquery metafields {\n\t\t\tshop {\n\t\t\t\twidgetPreRenderStyleTags: metafield(\n\t\t\t\t\tnamespace: "okendo"\n\t\t\t\t\tkey: "WidgetPreRenderStyleTags"\n\t\t\t\t) {\n\t\t\t\t\tvalue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t'),m=u?.value??"";return m||console.warn("Failed to retrieve pre-rendered widget style settings."),{reviewsHeaderConfig:s,cssVariables:a,customCss:d,initScriptContents:p,preRenderStyleTags:m,starSymbols:c}},o=({nonce:e,okendoProviderData:n,productUrlFormatter:r})=>{if(!n)return null;const{reviewsHeaderConfig:o,cssVariables:i,customCss:s,initScriptContents:a,preRenderStyleTags:d,starSymbols:c}=n,l="function"==typeof r?r.toString():"string"==typeof r?r:"(product) =>\n\t\t\t\t\tproduct && product.productHandle\n\t\t\t\t\t\t? \"/products/\" + product.productHandle + \"/\" + (product.variantId ? '?variantId=' + product.variantId : '')\n\t\t\t\t\t\t: undefined",p=(i??"").replace('<style id="oke-css-vars">',"").replace("</style>",""),u=s?s.replace('<style id="oke-reviews-custom-css">',"").replace("</style>",""):"";return t.createElement(t.Fragment,null,t.createElement("script",{nonce:e||"",id:"oke-reviews-settings",type:"application/json",dangerouslySetInnerHTML:{__html:JSON.stringify(o)}}),t.createElement("style",{nonce:e||"",id:"oke-css-vars",dangerouslySetInnerHTML:{__html:p}}),u&&t.createElement("style",{nonce:e||"",id:"oke-reviews-custom-css",dangerouslySetInnerHTML:{__html:u}}),a&&t.createElement("script",{nonce:e||"",dangerouslySetInnerHTML:{__html:a}}),t.createElement("script",{nonce:e||"",type:"text/javascript",dangerouslySetInnerHTML:{__html:`window.okeProductUrlFormatter = ${l.toString()}`}}),d&&t.createElement("div",{dangerouslySetInnerHTML:{__html:d}}),c&&t.createElement("div",{dangerouslySetInnerHTML:{__html:c}}))},i=({dataAttributes:r,metafieldContent:o=""})=>{const i=e(null),s=()=>{i.current&&window.okeWidgetApi.initWidget(i.current)};return n((()=>(window.okeWidgetApi&&i.current?s():document.addEventListener("oke-script-loaded",s),()=>{document.removeEventListener("oke-script-loaded",s)})),[r]),t.createElement("div",{ref:i,key:JSON.stringify(r),...r,dangerouslySetInnerHTML:o?{__html:o}:void 0})},s=/^[0-9]*$/;function a(t){if(t)return`shopify-${s.test(t)?t:t.split("/").slice(-1)[0]}`}const d=({productId:e,okendoReviewsSnippet:n})=>{const r={"data-oke-widget":"","data-oke-reviews-product-id":a(e)};return t.createElement(i,{dataAttributes:r,metafieldContent:n?.value})},c=({productId:e,okendoStarRatingSnippet:n})=>{const r={"data-oke-star-rating":"","data-oke-reviews-product-id":a(e)};return t.createElement(i,{dataAttributes:r,metafieldContent:n?.value})},l='#graphql\n\tfragment OkendoStarRatingSnippet on Product {\n\t\tokendoStarRatingSnippet: metafield(\n\t\t\tnamespace: "okendo"\n\t\t\tkey: "StarRatingSnippet"\n\t\t) {\n\t\t\tvalue\n\t\t}\n\t}\n',p='#graphql\n\tfragment OkendoReviewsSnippet on Product {\n\t\tokendoReviewsSnippet: metafield(\n\t\t\tnamespace: "okendo"\n\t\t\tkey: "ReviewsWidgetSnippet"\n\t\t) {\n\t\t\tvalue\n\t\t}\n\t}\n';export{p as OKENDO_PRODUCT_REVIEWS_FRAGMENT,l as OKENDO_PRODUCT_STAR_RATING_FRAGMENT,o as OkendoProvider,d as OkendoReviews,c as OkendoStarRating,r as getOkendoProviderData};
|
|
@@ -23,8 +23,21 @@ export declare const getOkendoProviderData: ({ context, subscriberId, apiDomain,
|
|
|
23
23
|
preRenderStyleTags: string;
|
|
24
24
|
starSymbols: string;
|
|
25
25
|
} | null>;
|
|
26
|
+
interface ReviewProduct {
|
|
27
|
+
productHandle?: string;
|
|
28
|
+
productId: string;
|
|
29
|
+
variantId?: string;
|
|
30
|
+
}
|
|
26
31
|
interface OkendoProviderProps {
|
|
32
|
+
/**
|
|
33
|
+
* A nonce that will be added to Okendo's scripts;
|
|
34
|
+
* this is required if Content Security Policy is set up on your store.
|
|
35
|
+
*/
|
|
36
|
+
nonce?: string;
|
|
37
|
+
/** The data returned by `getOkendoProviderData` */
|
|
27
38
|
okendoProviderData: Partial<OkendoProviderData> | null;
|
|
39
|
+
/** An optional product URL formatter */
|
|
40
|
+
productUrlFormatter?: (product: ReviewProduct) => string;
|
|
28
41
|
}
|
|
29
42
|
export declare const OkendoProvider: FC<OkendoProviderProps>;
|
|
30
43
|
export {};
|
|
@@ -5,7 +5,7 @@ export interface WithOkendoStarRatingSnippet {
|
|
|
5
5
|
}
|
|
6
6
|
interface OkendoStarRatingProps {
|
|
7
7
|
productId: string;
|
|
8
|
-
okendoStarRatingSnippet
|
|
8
|
+
okendoStarRatingSnippet?: MetafieldValue;
|
|
9
9
|
}
|
|
10
10
|
export declare const OkendoStarRating: FC<OkendoStarRatingProps>;
|
|
11
11
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -25,8 +25,21 @@ declare const getOkendoProviderData: ({ context, subscriberId, apiDomain, cdnDom
|
|
|
25
25
|
preRenderStyleTags: string;
|
|
26
26
|
starSymbols: string;
|
|
27
27
|
} | null>;
|
|
28
|
+
interface ReviewProduct {
|
|
29
|
+
productHandle?: string;
|
|
30
|
+
productId: string;
|
|
31
|
+
variantId?: string;
|
|
32
|
+
}
|
|
28
33
|
interface OkendoProviderProps {
|
|
34
|
+
/**
|
|
35
|
+
* A nonce that will be added to Okendo's scripts;
|
|
36
|
+
* this is required if Content Security Policy is set up on your store.
|
|
37
|
+
*/
|
|
38
|
+
nonce?: string;
|
|
39
|
+
/** The data returned by `getOkendoProviderData` */
|
|
29
40
|
okendoProviderData: Partial<OkendoProviderData> | null;
|
|
41
|
+
/** An optional product URL formatter */
|
|
42
|
+
productUrlFormatter?: (product: ReviewProduct) => string;
|
|
30
43
|
}
|
|
31
44
|
declare const OkendoProvider: FC<OkendoProviderProps>;
|
|
32
45
|
|
|
@@ -39,7 +52,7 @@ interface WithOkendoReviewsSnippet {
|
|
|
39
52
|
}
|
|
40
53
|
interface OkendoReviewsProps {
|
|
41
54
|
productId: string;
|
|
42
|
-
okendoReviewsSnippet
|
|
55
|
+
okendoReviewsSnippet?: MetafieldValue;
|
|
43
56
|
}
|
|
44
57
|
declare const OkendoReviews: FC<OkendoReviewsProps>;
|
|
45
58
|
|
|
@@ -48,11 +61,11 @@ interface WithOkendoStarRatingSnippet {
|
|
|
48
61
|
}
|
|
49
62
|
interface OkendoStarRatingProps {
|
|
50
63
|
productId: string;
|
|
51
|
-
okendoStarRatingSnippet
|
|
64
|
+
okendoStarRatingSnippet?: MetafieldValue;
|
|
52
65
|
}
|
|
53
66
|
declare const OkendoStarRating: FC<OkendoStarRatingProps>;
|
|
54
67
|
|
|
55
68
|
declare const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = "#graphql\n\tfragment OkendoStarRatingSnippet on Product {\n\t\tokendoStarRatingSnippet: metafield(\n\t\t\tnamespace: \"okendo\"\n\t\t\tkey: \"StarRatingSnippet\"\n\t\t) {\n\t\t\tvalue\n\t\t}\n\t}\n";
|
|
56
69
|
declare const OKENDO_PRODUCT_REVIEWS_FRAGMENT = "#graphql\n\tfragment OkendoReviewsSnippet on Product {\n\t\tokendoReviewsSnippet: metafield(\n\t\t\tnamespace: \"okendo\"\n\t\t\tkey: \"ReviewsWidgetSnippet\"\n\t\t) {\n\t\t\tvalue\n\t\t}\n\t}\n";
|
|
57
70
|
|
|
58
|
-
export { OKENDO_PRODUCT_REVIEWS_FRAGMENT, OKENDO_PRODUCT_STAR_RATING_FRAGMENT, OkendoProvider, OkendoReviews, OkendoStarRating, WithOkendoReviewsSnippet, WithOkendoStarRatingSnippet, getOkendoProviderData };
|
|
71
|
+
export { OKENDO_PRODUCT_REVIEWS_FRAGMENT, OKENDO_PRODUCT_STAR_RATING_FRAGMENT, OkendoProvider, OkendoReviews, OkendoStarRating, type WithOkendoReviewsSnippet, type WithOkendoStarRatingSnippet, getOkendoProviderData };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okendo/shopify-hydrogen",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Okendo React components for Shopify Hydrogen 2 (Remix)",
|
|
5
5
|
"author": "Okendo",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
@@ -15,32 +15,33 @@
|
|
|
15
15
|
"types": "dist/index.d.ts",
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "npm run eslint -- --max-warnings=0 && rimraf dist && rollup -c",
|
|
18
|
+
"prepack": "npm run build",
|
|
18
19
|
"eslint": "eslint src"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
|
-
"@okendo/reviews-common": "^5.
|
|
22
|
-
"@okendo/reviews-widget-plus": "^0.
|
|
22
|
+
"@okendo/reviews-common": "^5.163.1",
|
|
23
|
+
"@okendo/reviews-widget-plus": "^0.68.3",
|
|
23
24
|
"@okendo/tsconfig": "0.0.2",
|
|
24
|
-
"@rollup/plugin-commonjs": "^
|
|
25
|
-
"@rollup/plugin-node-resolve": "^15.
|
|
26
|
-
"@rollup/plugin-terser": "^0.4.
|
|
27
|
-
"@rollup/plugin-typescript": "^11.1.
|
|
28
|
-
"@types/react": "^18.
|
|
29
|
-
"@types/react-dom": "^18.
|
|
30
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
31
|
-
"@typescript-eslint/parser": "^
|
|
32
|
-
"eslint": "^8.
|
|
33
|
-
"eslint-plugin-react": "^7.
|
|
25
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
26
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
27
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
28
|
+
"@rollup/plugin-typescript": "^11.1.5",
|
|
29
|
+
"@types/react": "^18.2.45",
|
|
30
|
+
"@types/react-dom": "^18.2.18",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
|
32
|
+
"@typescript-eslint/parser": "^6.15.0",
|
|
33
|
+
"eslint": "^8.56.0",
|
|
34
|
+
"eslint-plugin-react": "^7.33.2",
|
|
34
35
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
35
|
-
"rimraf": "^5.0.
|
|
36
|
-
"rollup": "^
|
|
37
|
-
"rollup-plugin-dts": "^
|
|
36
|
+
"rimraf": "^5.0.5",
|
|
37
|
+
"rollup": "^4.9.1",
|
|
38
|
+
"rollup-plugin-dts": "^6.1.0",
|
|
38
39
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
39
|
-
"typescript": "^
|
|
40
|
+
"typescript": "^5.3.3"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
|
-
"@shopify/hydrogen": "
|
|
43
|
-
"@shopify/remix-oxygen": "
|
|
43
|
+
"@shopify/hydrogen": "*",
|
|
44
|
+
"@shopify/remix-oxygen": "*",
|
|
44
45
|
"react": "^18.2.0"
|
|
45
46
|
}
|
|
46
47
|
}
|