@okendo/shopify-hydrogen 1.0.1 → 1.0.2
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 +333 -146
- package/dist/esnext/client-components/OkendoClientStarRating.client.d.ts +4 -0
- package/dist/esnext/client-components/OkendoClientStarRating.client.js +25 -0
- package/dist/esnext/{components → client-components}/OkendoWidget.client.d.ts +1 -1
- package/dist/esnext/{components → client-components}/OkendoWidget.client.js +0 -0
- package/dist/esnext/client-components/index.d.ts +1 -0
- package/dist/esnext/client-components/index.js +1 -0
- package/dist/esnext/client.d.ts +1 -0
- package/dist/esnext/client.js +1 -0
- package/dist/esnext/components/OkendoProvider.server.js +2 -1
- package/dist/esnext/components/OkendoReviewsWidget.server.js +4 -2
- package/dist/esnext/components/OkendoStarRating.server.d.ts +1 -3
- package/dist/esnext/components/OkendoStarRating.server.js +4 -2
- package/dist/esnext/components/index.d.ts +1 -1
- package/dist/esnext/components/index.js +1 -1
- package/dist/esnext/fragments/index.d.ts +1 -0
- package/dist/esnext/fragments/index.js +11 -0
- package/dist/esnext/index.d.ts +3 -1
- package/dist/esnext/index.js +3 -1
- package/dist/esnext/models/starRating.d.ts +12 -0
- package/dist/esnext/models/starRating.js +1 -0
- package/dist/esnext/shared/{requestUtils.d.ts → server/requestUtils.d.ts} +1 -1
- package/dist/esnext/shared/{requestUtils.js → server/requestUtils.js} +0 -0
- package/dist/esnext/shared/sharedTypes.d.ts +4 -0
- package/package.json +13 -2
- package/dist/esnext/shared/index.d.ts +0 -4
- package/dist/esnext/shared/index.js +0 -4
package/README.md
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
This is the React component library to support Okendo Widget Plus Widgets in Shopify Hydrogen Projects.
|
|
4
4
|
|
|
5
|
-
Currently we provide
|
|
5
|
+
Currently we provide the following components:
|
|
6
|
+
|
|
7
|
+
### Server Components
|
|
6
8
|
|
|
7
9
|
1. [Reviews List](#components--okendo-reviews-widget)
|
|
8
10
|
2. [Star Ratings](#components--okendo-star-rating)
|
|
9
11
|
|
|
12
|
+
### Client Components
|
|
13
|
+
|
|
14
|
+
1. [Star Ratings](#components--okendo-client-star-rating)
|
|
15
|
+
|
|
10
16
|
<br/>
|
|
11
17
|
|
|
12
18
|
# Table of contents
|
|
@@ -17,7 +23,8 @@ Currently we provide server side components for 2 widgets:
|
|
|
17
23
|
- [Expose Shopify Metafields](#expose-shopify-metafields)
|
|
18
24
|
3. [How to Use Okendo Hydrogen Components In Your Hydrogen Apps](#how-to-use-okendo-hydrogen-components-in-your-hydrogen-app)
|
|
19
25
|
4. [Components](#components)
|
|
20
|
-
5. [
|
|
26
|
+
5. [GraphQL Fragments](#graphql-fragments)
|
|
27
|
+
6. [View Our Okendo Sample Hydrogen App](#view-our-okendo-sample-hydrogen-app)
|
|
21
28
|
|
|
22
29
|
<br/><br/>
|
|
23
30
|
|
|
@@ -43,7 +50,7 @@ The purpose of this documentation is to guide you on the following:
|
|
|
43
50
|
- You have an existing Shopify store.
|
|
44
51
|
- You have an existing Hydrogen app. ([Learn how to create a Hydrogen app](https://shopify.dev/custom-storefronts/hydrogen/getting-started/create))
|
|
45
52
|
- You have a current Okendo Reviews subscription and have the **Okendo: Product Reviews & UCG** app installed and configured.
|
|
46
|
-
- You have an existing Shopify custom app with Storefront access token. ([Learn how to configure Shopify Storefront](https://
|
|
53
|
+
- You have an existing Shopify custom app with Storefront access token. ([Learn how to configure Shopify Storefront](https://github.com/okendo/okendo-shopify-hydrogen-demo/wiki/Configure-Shopify-Storefront-API))
|
|
47
54
|
|
|
48
55
|
<br/>
|
|
49
56
|
|
|
@@ -73,7 +80,6 @@ The preferred method to expose Metafields is to [contact Okendo Support](mailto:
|
|
|
73
80
|
|
|
74
81
|
## Exposing Metafields via GraphQL
|
|
75
82
|
|
|
76
|
-
|
|
77
83
|
### Using Curl
|
|
78
84
|
|
|
79
85
|
You can also expose the required Okendo Shopify Metafields by using GraphQL with curl.
|
|
@@ -194,100 +200,97 @@ mutation {
|
|
|
194
200
|
### Using GraphQL IDE
|
|
195
201
|
|
|
196
202
|
1. Open your GraphQL IDE (such as Postman) and make a `POST` request with the following details:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
203
|
+
- **URL:** https://{shop}.myshopify.com/admin/api/2022-04/graphql.json
|
|
204
|
+
- **Headers:** - X-Shopify-Access-Token: {access_token} - Content-Type: application/json
|
|
205
|
+
2. Execute the following request to expose the `WidgetPreRenderStyleTag` Shop Metafield.
|
|
206
|
+
|
|
207
|
+
```graphql
|
|
208
|
+
mutation {
|
|
209
|
+
metafieldStorefrontVisibilityCreate(
|
|
210
|
+
input: {
|
|
211
|
+
namespace: "okendo"
|
|
212
|
+
key: "WidgetPreRenderStyleTags"
|
|
213
|
+
ownerType: SHOP
|
|
214
|
+
}
|
|
215
|
+
) {
|
|
216
|
+
metafieldStorefrontVisibility {
|
|
217
|
+
id
|
|
218
|
+
}
|
|
219
|
+
userErrors {
|
|
220
|
+
field
|
|
221
|
+
message
|
|
216
222
|
}
|
|
217
223
|
}
|
|
218
|
-
|
|
224
|
+
}
|
|
225
|
+
```
|
|
219
226
|
|
|
220
|
-
|
|
227
|
+
3. Execute the following request to expose the `WidgetPreRenderBodyStyleTags` Shop Metafield.
|
|
221
228
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
229
|
+
```graphql
|
|
230
|
+
mutation {
|
|
231
|
+
metafieldStorefrontVisibilityCreate(
|
|
232
|
+
input: {
|
|
233
|
+
namespace: "okendo"
|
|
234
|
+
key: "WidgetPreRenderBodyStyleTags"
|
|
235
|
+
ownerType: SHOP
|
|
236
|
+
}
|
|
237
|
+
) {
|
|
238
|
+
metafieldStorefrontVisibility {
|
|
239
|
+
id
|
|
240
|
+
}
|
|
241
|
+
userErrors {
|
|
242
|
+
field
|
|
243
|
+
message
|
|
238
244
|
}
|
|
239
245
|
}
|
|
240
|
-
|
|
246
|
+
}
|
|
247
|
+
```
|
|
241
248
|
|
|
242
|
-
|
|
249
|
+
4. Execute the following request to expose the `ReviewsWidgetSnippet` Product Metafield.
|
|
243
250
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
251
|
+
```graphql
|
|
252
|
+
mutation {
|
|
253
|
+
metafieldStorefrontVisibilityCreate(
|
|
254
|
+
input: {
|
|
255
|
+
namespace: "okendo"
|
|
256
|
+
key: "ReviewsWidgetSnippet"
|
|
257
|
+
ownerType: PRODUCT
|
|
258
|
+
}
|
|
259
|
+
) {
|
|
260
|
+
metafieldStorefrontVisibility {
|
|
261
|
+
id
|
|
262
|
+
}
|
|
263
|
+
userErrors {
|
|
264
|
+
field
|
|
265
|
+
message
|
|
260
266
|
}
|
|
261
267
|
}
|
|
262
|
-
|
|
268
|
+
}
|
|
269
|
+
```
|
|
263
270
|
|
|
264
|
-
|
|
271
|
+
5. Execute the following request to expose the `StarRatingSnippet` the Product Metafield.
|
|
265
272
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
userErrors {
|
|
279
|
-
field
|
|
280
|
-
message
|
|
281
|
-
}
|
|
273
|
+
```graphql
|
|
274
|
+
mutation {
|
|
275
|
+
metafieldStorefrontVisibilityCreate(
|
|
276
|
+
input: { namespace: "okendo", key: "StarRatingSnippet", ownerType: PRODUCT }
|
|
277
|
+
) {
|
|
278
|
+
metafieldStorefrontVisibility {
|
|
279
|
+
id
|
|
280
|
+
}
|
|
281
|
+
userErrors {
|
|
282
|
+
field
|
|
283
|
+
message
|
|
282
284
|
}
|
|
283
285
|
}
|
|
284
|
-
|
|
286
|
+
}
|
|
287
|
+
```
|
|
285
288
|
|
|
286
|
-
|
|
289
|
+
**References**
|
|
287
290
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
+
- [https://shopify.dev/api/examples/metafields#step-1-expose-metafields](https://shopify.dev/api/examples/metafields#step-1-expose-metafields)
|
|
292
|
+
- [https://shopify.dev/api/admin-graphql/2022-04/mutations/metafieldstorefrontvisibilitycreate](https://shopify.dev/api/admin-graphql/2022-04/mutations/metafieldstorefrontvisibilitycreate)
|
|
293
|
+
</details>
|
|
291
294
|
|
|
292
295
|
<br/><br/>
|
|
293
296
|
|
|
@@ -298,77 +301,76 @@ mutation {
|
|
|
298
301
|
1. In your Hydrogen app directory, run `npm install @okendo/shopify-hydrogen` inside a terminal or PowerShell window.
|
|
299
302
|
2. **Optional:** Create (or add to) your `.env` file at the top level of your project (next to **hydrogen.config.ts**) the following, where `<your_subscriber_id>` is replaced with your Okendo Subscriber ID:
|
|
300
303
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
```sh
|
|
305
|
+
# .env
|
|
306
|
+
VITE_OKENDO_SUBSCRIBER_ID=<your_subscriber_id>
|
|
307
|
+
```
|
|
305
308
|
|
|
306
309
|
3. Open **vite.config.ts** and add `import okendo from '@okendo/shopify-hydrogen/plugin';` to the list of imports.
|
|
307
310
|
4. Add `okendo()` to the list of `plugins`.
|
|
308
311
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
```
|
|
312
|
+
```tsx
|
|
313
|
+
/* vite.config.ts */
|
|
314
|
+
/// <reference types="vitest" />
|
|
315
|
+
import { defineConfig } from "vite";
|
|
316
|
+
import hydrogen from "@shopify/hydrogen/plugin";
|
|
317
|
+
import okendo from "@okendo/shopify-hydrogen/plugin";
|
|
318
|
+
|
|
319
|
+
export default defineConfig({
|
|
320
|
+
plugins: [hydrogen(), okendo()],
|
|
321
|
+
resolve: {
|
|
322
|
+
alias: [{ find: /^~\/(.*)/, replacement: "/src/$1" }],
|
|
323
|
+
},
|
|
324
|
+
optimizeDeps: {
|
|
325
|
+
include: ["@headlessui/react", "clsx", "react-use", "typographic-base"],
|
|
326
|
+
},
|
|
327
|
+
test: {
|
|
328
|
+
globals: true,
|
|
329
|
+
testTimeout: 10000,
|
|
330
|
+
hookTimeout: 10000,
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
```
|
|
332
334
|
|
|
333
335
|
5. Open **App.server.tsx** and import `OkendoProvider`.
|
|
334
336
|
6. Include the `OkendoProvider` as shown below, passing through your `subscriberId` from your Vite environment variables.
|
|
335
337
|
|
|
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
|
-
```
|
|
338
|
+
```tsx
|
|
339
|
+
/* App.server.tsx */
|
|
340
|
+
import {OkendoProvider} from '@okendo/shopify-hydrogen';
|
|
341
|
+
|
|
342
|
+
function App() {
|
|
343
|
+
return (
|
|
344
|
+
<Suspense fallback={<LoadingFallback />}>
|
|
345
|
+
<ShopifyProvider>
|
|
346
|
+
<!-- *** Include OkendoProvider HERE *** -->
|
|
347
|
+
<OkendoProvider
|
|
348
|
+
subscriberId={import.meta.env.VITE_OKENDO_SUBSCRIBER_ID}
|
|
349
|
+
/>
|
|
350
|
+
<ServerCartProvider>
|
|
351
|
+
<DefaultSeo />
|
|
352
|
+
<Router>
|
|
353
|
+
<FileRoutes />
|
|
354
|
+
<Route path="*" page={<NotFound />} />
|
|
355
|
+
</Router>
|
|
356
|
+
</ServerCartProvider>
|
|
357
|
+
<PerformanceMetrics />
|
|
358
|
+
{import.meta.env.DEV && <PerformanceMetricsDebug />}
|
|
359
|
+
</ShopifyProvider>
|
|
360
|
+
</Suspense>
|
|
361
|
+
);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
If your app doesn't use Vite environment variables from a `.env` file, you could also provide your Okendo Subscriber ID through other means, i.e. directly:
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
<OkendoProvider subscriberId={okendoSubscriberId} />
|
|
368
|
+
```
|
|
369
369
|
|
|
370
370
|
## Widget Usage
|
|
371
371
|
|
|
372
|
+
### Server Components
|
|
373
|
+
|
|
372
374
|
Import `OkendoReviewsWidget` and `OkendoStarRating` and use as TSX components. Pass in the Shopify Product ID as a prop.
|
|
373
375
|
|
|
374
376
|
The `productId` prop is optional for the `OkendoReviewsWidget`. Not providing it will mean that the widget will display reviews for all products, which is ideal for homepages or collection pages.
|
|
@@ -385,7 +387,27 @@ const okendoReviewsWidget = <OkendoReviewsWidget productId={product.id} />;
|
|
|
385
387
|
const okendoStarRating = <OkendoStarRating productId={product.id} />;
|
|
386
388
|
```
|
|
387
389
|
|
|
388
|
-
> ℹ️ Okendo widgets are server components. If you want to use them within a client component, you must pass the widget components as props to your client component. Widget components can be used directly in a server component. [Learn more here](
|
|
390
|
+
> ℹ️ Okendo `OkendoReviewsWidget` and `OkendoStarRating` widgets are server components. If you want to use them within a client component, you must pass the widget components as props to your client component. Widget components can be used directly in a server component. [Learn more here](#graphql-fragments).
|
|
391
|
+
|
|
392
|
+
<br />
|
|
393
|
+
|
|
394
|
+
### Client Components
|
|
395
|
+
|
|
396
|
+
#### Star Rating Widget Usage in Client Components
|
|
397
|
+
|
|
398
|
+
We also include an `OkendoClientStarRating` component for use within client-side (e.g.`<your component>.client.tsx`) components.
|
|
399
|
+
|
|
400
|
+
Import the `OkendoClientStarRating` component inside a client component and use as a TSX component as seen below:
|
|
401
|
+
|
|
402
|
+
```tsx
|
|
403
|
+
import { OkendoClientStarRating } from '@okendo/shopify-hydrogen';
|
|
404
|
+
|
|
405
|
+
...
|
|
406
|
+
|
|
407
|
+
const okendoClientStarRating = <OkendoClientStarRating productId={product.id} />;
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
> ℹ️ OPTIONAL: You can render the `OkendoClientStarRating` component without making any client-side network requests by using an optional prop `okendoStarRatingSnippet`. This can be achieved using a GraphQL Fragment. [Learn more here](#graphql-fragments).
|
|
389
411
|
|
|
390
412
|
<br />
|
|
391
413
|
|
|
@@ -418,18 +440,183 @@ It will provide:
|
|
|
418
440
|
|
|
419
441
|
The Okendo Reviews List widget.
|
|
420
442
|
|
|
421
|
-
| Name
|
|
422
|
-
|
|
|
443
|
+
| Name | Type | Description | Required |
|
|
444
|
+
| ---------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
|
|
423
445
|
| <code>productId</code> | <code>string</code> | The Shopify Product ID. If provided, the Reviews Widget will be configured to display reviews specific to that product. Otherwise, the Reviews Widget will display reviews for all products. | no |
|
|
424
446
|
|
|
425
447
|
### OkendoStarRating<a id="components--okendo-star-rating" name="components--okendo-star-rating"></a>
|
|
426
|
-
|
|
448
|
+
|
|
449
|
+
The Okendo Star Rating widget - **For use in _server_ components**.
|
|
427
450
|
|
|
428
451
|
| Name | Type | Description | Required |
|
|
429
452
|
| ---------------------- | ------------------- | ----------------------- | -------- |
|
|
430
453
|
| <code>productId</code> | <code>string</code> | The Shopify Product ID. | yes |
|
|
454
|
+
|
|
455
|
+
<br/>
|
|
456
|
+
|
|
457
|
+
### OkendoClientStarRating<a id="components--okendo-client-star-rating" name="components--okendo-client-star-rating"></a>
|
|
458
|
+
|
|
459
|
+
The Okendo Star Rating widget - **For use in _client_ components**.
|
|
460
|
+
|
|
461
|
+
| Name | Type | Description | Required |
|
|
462
|
+
| ------------------------------------ | -------------------------------------------------- | --------------------------------------------------------------- | -------- |
|
|
463
|
+
| <code>productId</code> | <code>string</code> | The Shopify Product ID. | yes |
|
|
464
|
+
| <code>okendoStarRatingSnippet</code> | <code>Pick<Metafield, "value"> \| undefined</code> | The server-side pre-rendered markup representing a star snippet | no |
|
|
465
|
+
|
|
466
|
+
<br/>
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
# GraphQL Fragments <a id="graphql-fragments" name="graphql-fragments"></a>
|
|
471
|
+
|
|
472
|
+
A GraphQL fragment is a piece of logic that can be shared between multiple queries and mutations.
|
|
473
|
+
|
|
474
|
+
We offer performance benefits even when using our client Star Rating Widget by exposing an `OKENDO_PRODUCT_STAR_RATING_FRAGMENT` GraphQL Fragment for use in your server components.
|
|
475
|
+
|
|
476
|
+
This allows you to fetch pre-rendered markup in a **server component** for use within the client component (usually passed down as a property from the server component to the client).
|
|
477
|
+
|
|
478
|
+
If the pre-rendered mark-up is present, the star rating widget initialization does not occur in the client, offering performance benefits.
|
|
479
|
+
|
|
431
480
|
<br/>
|
|
432
481
|
|
|
482
|
+
## Example: Using GraphQL Framgment with OkendoClientStarRating
|
|
483
|
+
|
|
484
|
+
> ℹ️ NOTE: The following example usage is based on our Okendo Hydrogen Demo Store. Refer to the [Github repo](https://github.com/okendo/okendo-shopify-hydrogen-demo) to see a working example.
|
|
485
|
+
|
|
486
|
+
<br/>
|
|
487
|
+
|
|
488
|
+
In the sample [Okendo Hydrogen Demo Store](https://github.com/okendo/okendo-shopify-hydrogen-demo) we extend the `PRODUCT_CARD_FRAGMENT` with our `OKENDO_PRODUCT_STAR_RATING_FRAGMENT`.
|
|
489
|
+
|
|
490
|
+
Open `ProductGrid.client.tsx` and make the changes to the `PRODUCT_CARD_FRAGMENT` as seen below:
|
|
491
|
+
|
|
492
|
+
<br/>
|
|
493
|
+
|
|
494
|
+
**Before**
|
|
495
|
+
|
|
496
|
+
```tsx
|
|
497
|
+
export const PRODUCT_CARD_FRAGMENT = gql`
|
|
498
|
+
fragment ProductCard on Product {
|
|
499
|
+
id
|
|
500
|
+
title
|
|
501
|
+
publishedAt
|
|
502
|
+
handle
|
|
503
|
+
variants(first: 1) {
|
|
504
|
+
nodes {
|
|
505
|
+
id
|
|
506
|
+
image {
|
|
507
|
+
url
|
|
508
|
+
altText
|
|
509
|
+
width
|
|
510
|
+
height
|
|
511
|
+
}
|
|
512
|
+
priceV2 {
|
|
513
|
+
amount
|
|
514
|
+
currencyCode
|
|
515
|
+
}
|
|
516
|
+
compareAtPriceV2 {
|
|
517
|
+
amount
|
|
518
|
+
currencyCode
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
`;
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**After**
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
export const PRODUCT_CARD_FRAGMENT = gql`
|
|
530
|
+
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
|
|
531
|
+
fragment ProductCard on Product {
|
|
532
|
+
id
|
|
533
|
+
title
|
|
534
|
+
publishedAt
|
|
535
|
+
handle
|
|
536
|
+
...OkendoStarRatingSnippet
|
|
537
|
+
variants(first: 1) {
|
|
538
|
+
nodes {
|
|
539
|
+
id
|
|
540
|
+
image {
|
|
541
|
+
url
|
|
542
|
+
altText
|
|
543
|
+
width
|
|
544
|
+
height
|
|
545
|
+
}
|
|
546
|
+
priceV2 {
|
|
547
|
+
amount
|
|
548
|
+
currencyCode
|
|
549
|
+
}
|
|
550
|
+
compareAtPriceV2 {
|
|
551
|
+
amount
|
|
552
|
+
currencyCode
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
`;
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Adding the `OKENDO_PRODUCT_STAR_RATING_FRAGMENT` GraphQL Fragment allows you to access and include an `okendoStarRatingSnippet` in your `Product` query.
|
|
561
|
+
|
|
562
|
+
The `okendoStarRatingSnippet` is a server-side rendered version of the output of your star rating contents. This is supplied to your `Product`/`Collection` queries using the `OKENDO_PRODUCT_STAR_RATING_FRAGMENT` to fetch a metafield containg the markup.
|
|
563
|
+
|
|
564
|
+
To help with typing we have exposed an `OkendoProductFragment` type which can be used as a union type with an existing Hydrogen `Product` type.
|
|
565
|
+
|
|
566
|
+
For this example we have used `ProductCard.client.tsx` from our Okendo Hydrogen Demo App.
|
|
567
|
+
|
|
568
|
+
The `ProductCard.client.tsx` component has the result of a server-side `useShopQuery` passed down as props to it.
|
|
569
|
+
|
|
570
|
+
Because we have implemented our `OKENDO_PRODUCT_STAR_RATING_FRAGMENT` we can express the resulting type as a union:
|
|
571
|
+
|
|
572
|
+
```tsx
|
|
573
|
+
Product & OkendoProductFragment;
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
In the `ProductCard.client.tsx` import the `OkendoProductFragment` type and the `OkendoClientStarRating` component:
|
|
577
|
+
|
|
578
|
+
```tsx
|
|
579
|
+
import type { OkendoProductFragment } from "@okendo/shopify-hydrogen";
|
|
580
|
+
import { OkendoClientStarRating } from "@okendo/shopify-hydrogen/client";
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
The constructor of the `ProductCard.client.tsx` component can then be modified to use a union-type for the `Product` being passed in to the constructor:
|
|
584
|
+
|
|
585
|
+
```tsx
|
|
586
|
+
export function ProductCard({
|
|
587
|
+
product,
|
|
588
|
+
label,
|
|
589
|
+
className,
|
|
590
|
+
loading,
|
|
591
|
+
onClick,
|
|
592
|
+
}: {
|
|
593
|
+
product: Product & OkendoProductFragment;
|
|
594
|
+
label?: string;
|
|
595
|
+
className?: string;
|
|
596
|
+
loading?: HTMLImageElement['loading'];
|
|
597
|
+
onClick?: () => void;
|
|
598
|
+
}) {
|
|
599
|
+
...
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
Import the `OkendoClientStarRating` component inside the client component and use as seen below. Pass in `product.okendoStarRatingSnippet` as the value for the optional prop `okendoStarRatingSnippet`. This will use the pre-rendered value of the star rating markup without having to make any client-side network requests.
|
|
604
|
+
|
|
605
|
+
```tsx
|
|
606
|
+
{
|
|
607
|
+
product.okendoStarRatingSnippet?.value ? (
|
|
608
|
+
<OkendoClientStarRating
|
|
609
|
+
productId={product.id}
|
|
610
|
+
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
|
|
611
|
+
/>
|
|
612
|
+
) : null;
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**References**
|
|
617
|
+
|
|
618
|
+
- [GraphQL Fragments](https://www.apollographql.com/docs/react/data/fragments/)
|
|
619
|
+
|
|
433
620
|
---
|
|
434
621
|
|
|
435
622
|
<br/>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { OkendoWidgetClient } from './OkendoWidget.client';
|
|
3
|
+
import { getOkendoProductId } from '../shared/productUtils';
|
|
4
|
+
import { widgetConfigurationError, widgetMetafieldError } from '../shared/errorUtils';
|
|
5
|
+
export const OkendoClientStarRating = (props) => {
|
|
6
|
+
const { productId, okendoStarRatingSnippet } = props;
|
|
7
|
+
const okendoProductId = getOkendoProductId(productId);
|
|
8
|
+
if (!okendoProductId) {
|
|
9
|
+
console.error(widgetConfigurationError('OkendoClientStarRating', 'productId was not provided'));
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
if (!okendoStarRatingSnippet?.value) {
|
|
13
|
+
console.warn(widgetMetafieldError('OkendoStarRating', 'StarRatingSnippet'));
|
|
14
|
+
}
|
|
15
|
+
const dataAttributes = {
|
|
16
|
+
'data-oke-star-rating': '',
|
|
17
|
+
'data-oke-reviews-product-id': okendoProductId,
|
|
18
|
+
'data-oke-scroll-disabled': 'true'
|
|
19
|
+
};
|
|
20
|
+
if (okendoStarRatingSnippet?.value) {
|
|
21
|
+
dataAttributes['data-oke-rendered'] = okendoStarRatingSnippet.value;
|
|
22
|
+
}
|
|
23
|
+
return (React.createElement(OkendoWidgetClient, { dataAttributes: dataAttributes, metafieldContent: okendoStarRatingSnippet?.value }));
|
|
24
|
+
};
|
|
25
|
+
export default OkendoClientStarRating;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ReviewsWidgetPlus } from '@okendo/reviews-widget-plus/dist-utils/ReviewsWidgetPlus';
|
|
3
|
-
import type { SKO } from '../shared';
|
|
3
|
+
import type { SKO } from '../shared/sharedTypes';
|
|
4
4
|
export declare const OkendoWidgetClient: React.FunctionComponent<OkendoWidgetClientProps>;
|
|
5
5
|
export interface OkendoWidgetClientProps {
|
|
6
6
|
dataAttributes: SKO;
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './OkendoClientStarRating.client';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './OkendoClientStarRating.client';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './client-components';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './client-components';
|
|
@@ -2,7 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import parse from 'html-react-parser';
|
|
3
3
|
import { fetchSync, useShopQuery, gql, CacheShort } from '@shopify/hydrogen';
|
|
4
4
|
import { Head } from '@shopify/hydrogen';
|
|
5
|
-
import { okendoError
|
|
5
|
+
import { okendoError } from '../shared/errorUtils';
|
|
6
|
+
import { setInOkendoRequestContext } from '../shared/server/requestUtils';
|
|
6
7
|
const kDefaultOkendoApiDomain = 'api.okendo.io/v1';
|
|
7
8
|
const kDefaultOkendoCdnDomain = 'cdn-static.okendo.io';
|
|
8
9
|
export const OkendoProvider = (props) => {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import parse from 'html-react-parser';
|
|
3
3
|
import { useShopQuery, gql } from '@shopify/hydrogen';
|
|
4
|
-
import { OkendoWidgetClient } from '
|
|
5
|
-
import {
|
|
4
|
+
import { OkendoWidgetClient } from '../client-components/OkendoWidget.client';
|
|
5
|
+
import { widgetMetafieldError } from '../shared/errorUtils';
|
|
6
|
+
import { getOkendoProductId } from '../shared/productUtils';
|
|
7
|
+
import { useOkendoRequestContext } from '../shared/server/requestUtils';
|
|
6
8
|
export const OkendoReviewsWidget = (props) => {
|
|
7
9
|
const { setupFailed } = useOkendoRequestContext();
|
|
8
10
|
if (setupFailed) {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { OkendoStarRatingProps } from '../models/starRating';
|
|
2
3
|
export declare const OkendoStarRating: React.FunctionComponent<OkendoStarRatingProps>;
|
|
3
|
-
interface OkendoStarRatingProps {
|
|
4
|
-
productId: string;
|
|
5
|
-
}
|
|
6
4
|
export default OkendoStarRating;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useShopQuery, gql } from '@shopify/hydrogen';
|
|
3
|
-
import { OkendoWidgetClient } from '
|
|
4
|
-
import {
|
|
3
|
+
import { OkendoWidgetClient } from '../client-components/OkendoWidget.client';
|
|
4
|
+
import { widgetConfigurationError, widgetMetafieldError } from '../shared/errorUtils';
|
|
5
|
+
import { getOkendoProductId } from '../shared/productUtils';
|
|
6
|
+
import { useOkendoRequestContext } from '../shared/server/requestUtils';
|
|
5
7
|
export const OkendoStarRating = (props) => {
|
|
6
8
|
const { setupFailed } = useOkendoRequestContext();
|
|
7
9
|
if (setupFailed) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const OKENDO_PRODUCT_STAR_RATING_FRAGMENT: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { gql } from '@shopify/hydrogen';
|
|
2
|
+
export const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = gql `
|
|
3
|
+
fragment OkendoStarRatingSnippet on Product {
|
|
4
|
+
okendoStarRatingSnippet: metafield(
|
|
5
|
+
namespace: "okendo"
|
|
6
|
+
key: "StarRatingSnippet"
|
|
7
|
+
) {
|
|
8
|
+
value
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
`;
|
package/dist/esnext/index.d.ts
CHANGED
package/dist/esnext/index.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Metafield } from '@shopify/hydrogen/dist/esnext/storefront-api-types';
|
|
2
|
+
export interface OkendoClientStarRatingProps extends OkendoStarRatingProps {
|
|
3
|
+
okendoStarRatingSnippet?: Pick<Metafield, 'value'>;
|
|
4
|
+
}
|
|
5
|
+
export interface OkendoStarRatingProps {
|
|
6
|
+
productId: string;
|
|
7
|
+
}
|
|
8
|
+
export interface OkendoStarRatingMetafields {
|
|
9
|
+
product: {
|
|
10
|
+
starRatingSnippet?: Pick<Metafield, 'value'>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SKO } from '
|
|
1
|
+
import type { SKO } from '../sharedTypes';
|
|
2
2
|
export declare function setInOkendoRequestContext(key: keyof OkendoRequestContextKeys, value: RequestContextSafeType | RequestContextSafeType[]): void;
|
|
3
3
|
export declare function useOkendoRequestContext(): OkendoRequestContext;
|
|
4
4
|
interface OkendoRequestContextKeys {
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okendo/shopify-hydrogen",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A component library containing Okendo Reviews React components.",
|
|
5
5
|
"main": "dist/esnext/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -12,6 +12,16 @@
|
|
|
12
12
|
"import": "./dist/esnext/framework/plugins/plugin.js",
|
|
13
13
|
"require": "./dist/node/framework/plugins/plugin.js"
|
|
14
14
|
},
|
|
15
|
+
"./client": {
|
|
16
|
+
"import": {
|
|
17
|
+
"types": "./dist/esnext/client.d.ts",
|
|
18
|
+
"default": "./dist/esnext/client.js"
|
|
19
|
+
},
|
|
20
|
+
"node": {
|
|
21
|
+
"types": "./dist/esnext/client.d.ts",
|
|
22
|
+
"default": "./dist/esnext/client.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
15
25
|
"./plugin.cjs": "./plugin.cjs",
|
|
16
26
|
"./package.json": "./package.json",
|
|
17
27
|
"./*": "./dist/esnext/*.js"
|
|
@@ -40,8 +50,9 @@
|
|
|
40
50
|
"@types/react": "^18.0.14",
|
|
41
51
|
"@types/react-dom": "^18.0.5",
|
|
42
52
|
"dotenv": "^16.0.1",
|
|
53
|
+
"ncp": "^2.0.0",
|
|
43
54
|
"rimraf": "^3.0.2",
|
|
44
|
-
"typescript": "^4.7.
|
|
55
|
+
"typescript": "^4.7.4",
|
|
45
56
|
"vite": "^2.9.0"
|
|
46
57
|
},
|
|
47
58
|
"peerDependencies": {
|