2.1.5 • Published 20 days ago

@okendo/shopify-hydrogen v2.1.5

Weekly downloads
-
License
SEE LICENSE IN LI...
Repository
-
Last release
20 days ago

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.

Okendo Hydrogen 2 (Remix) React Components

This package brings Okendo's review widgets to a Shopify Hydrogen store.

Requirements

Demo Store

Our demo store, which is based on the demo store provided by Shopify, can be found here.

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.

How it works

This package provides:

  • one function: getOkendoProviderData
  • three React components: OkendoProvider, OkendoStarRating, and OkendoReviews

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.

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.

Expose Shopify Metafields

Okendo Reviews use Product and Shop metafields. You will need to expose these metafields so that they can be retrieved by your Hydrogen app.

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.

Exposing Metafields via GraphQL

Using Curl

You can also expose the required Okendo Shopify metafields by using GraphQL with curl.

  1. Open a new terminal or PowerShell window.
  2. Run the following command to expose the WidgetPreRenderStyleTags shop metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "WidgetPreRenderStyleTags"
			ownerType: SHOP
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
'
  1. Run the following command to expose the WidgetPreRenderBodyStyleTags shop metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "WidgetPreRenderBodyStyleTags"
			ownerType: SHOP
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
'
  1. Run the following command to expose the ReviewsWidgetSnippet product metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "ReviewsWidgetSnippet"
			ownerType: PRODUCT
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
'
  1. Run the following command to expose the StarRatingSnippet the product metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "StarRatingSnippet"
			ownerType: PRODUCT
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
'

Using GraphQL IDE

  1. Open your GraphQL IDE (such as Postman) and make a POST request with the following details:
  1. Execute the following request to expose the WidgetPreRenderStyleTags shop metafield:
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "WidgetPreRenderStyleTags"
			ownerType: SHOP
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
  1. Execute the following request to expose the WidgetPreRenderBodyStyleTags shop metafield:
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "WidgetPreRenderBodyStyleTags"
			ownerType: SHOP
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
  1. Execute the following request to expose the ReviewsWidgetSnippet product metafield:
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "ReviewsWidgetSnippet"
			ownerType: PRODUCT
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
  1. Execute the following request to expose the StarRatingSnippet the product metafield:
mutation {
	metafieldStorefrontVisibilityCreate(
		input: { namespace: "okendo", key: "StarRatingSnippet", ownerType: PRODUCT }
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}

References

Installation

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). You will find the following steps already done in our demo store.

Run:

npm i @okendo/shopify-hydrogen

app/root.tsx

Open app/root.tsx and add the following import:

import {
	OkendoProvider,
	getOkendoProviderData,
} from "@okendo/shopify-hydrogen";

Locate the loader function, append okendoProviderData to the returned data as shown below, and set subscriberId to your Okendo subscriber ID:

return defer(
	{
		...
		okendoProviderData: await getOkendoProviderData({
			context,
			subscriberId: "<your-okendo-subscriber-id>",
		}),
	}
);

Locate the App function, add the meta tag oke:subscriber_id to head, and place your Okendo subscriber ID in its content:

<head>
	<meta charSet="utf-8" />
	<meta name="viewport" content="width=device-width,initial-scale=1" />
	<meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
	...

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):

...
<body>
	<OkendoProvider
		nonce={nonce}
		okendoProviderData={data.okendoProviderData}
	/>
	...
</body>
...

app/entry.server.tsx

This is only necessary if Content Security Policy is active in your project.

Locate the call to createContentSecurityPolicy, and add:

  • https://d3hw6dc1ow8pp2.cloudfront.net, https://d3g5hqndtiniji.cloudfront.net, and data: to defaultSrc
  • https://d3hw6dc1ow8pp2.cloudfront.net to styleSrc
  • https://api.okendo.io to connectSrc

Note that it's necessary to to add the default values ('self', etc.) when extending the CSP. The call to createContentSecurityPolicy should now look like the following:

const { nonce, header, NonceProvider } = createContentSecurityPolicy({
	defaultSrc: [
		"'self'",
		"localhost:*",
		"https://cdn.shopify.com",
		"https://d3hw6dc1ow8pp2.cloudfront.net",
		"https://d3g5hqndtiniji.cloudfront.net",
		"https://cdn-static.okendo.io",
		"https://surveys.okendo.io",
		"data:",
	],
	imgSrc: [
		"'self'",
		"https://cdn.shopify.com",
		"data:",
		"https://d3hw6dc1ow8pp2.cloudfront.net",
		"https://d3g5hqndtiniji.cloudfront.net",
		"https://cdn-static.okendo.io",
		"https://surveys.okendo.io",
	],
	mediaSrc: [
		"'self'",
		"https://d3hw6dc1ow8pp2.cloudfront.net",
		"https://d3g5hqndtiniji.cloudfront.net",
		"https://cdn-static.okendo.io"
	],
	styleSrcElem: [
		"'self'",
		"'unsafe-inline'",
		"https://cdn.shopify.com",
		"https://fonts.googleapis.com",
		"https://fonts.gstatic.com",
		"https://d3hw6dc1ow8pp2.cloudfront.net",
		"https://cdn-static.okendo.io",
		"https://surveys.okendo.io",
	],
	scriptSrc: [
		"'self'",
		"https://cdn.shopify.com",
		"https://d3hw6dc1ow8pp2.cloudfront.net",
		"https://cdn-static.okendo.io",
		"https://surveys.okendo.io",
	],
	fontSrc: [
		"'self'",
		"https://fonts.gstatic.com",
		"https://d3hw6dc1ow8pp2.cloudfront.net",
		"https://cdn.shopify.com",
		"https://cdn-static.okendo.io",
		"https://surveys.okendo.io",
	],
	connectSrc: [
		"'self'",
		"https://monorail-edge.shopifysvc.com",
		"localhost:*",
		"ws://localhost:*",
		"ws://127.0.0.1:*",
		"https://api.okendo.io",
		"https://cdn-static.okendo.io",
		"https://surveys.okendo.io",
	],
});

app/routes/_index.tsx

Add the following imports:

import {
	OkendoStarRating,
	type WithOkendoStarRatingSnippet,
} from "@okendo/shopify-hydrogen";

Add the following block just before the RECOMMENDED_PRODUCTS_QUERY GraphQL query:

const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
	fragment OkendoStarRatingSnippet on Product {
		okendoStarRatingSnippet: metafield(
			namespace: "okendo"
			key: "StarRatingSnippet"
		) {
			value
		}
	}
` as const;

Then append ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT} and ...OkendoStarRatingSnippet to RECOMMENDED_PRODUCTS_QUERY:

const RECOMMENDED_PRODUCTS_QUERY = `#graphql
	${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
	fragment RecommendedProduct on Product {
		id
		title
		handle
		priceRange {
			minVariantPrice {
				amount
				currencyCode
			}
		}
		images(first: 1) {
			nodes {
				id
				url
				altText
				width
				height
			}
		}
		...OkendoStarRatingSnippet
	}
	query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
		@inContext(country: $country, language: $language) {
		products(first: 4, sortKey: UPDATED_AT, reverse: true) {
			nodes {
				...RecommendedProduct
			}
		}
	}
` as const;

Tweak the type of the products prop of RecommendedProducts:

products: Promise<{
	products: {
		nodes: (RecommendedProductsQuery["products"]["nodes"][0] &
			WithOkendoStarRatingSnippet)[];
	};
}>;

Add OkendoStarRating to RecommendedProducts:

<OkendoStarRating
	productId={product.id}
	okendoStarRatingSnippet={product.okendoStarRatingSnippet}
/>

For instance, we can add it below the product title, like this:

<Image
	data={product.images.nodes[0]}
	aspectRatio="1/1"
	sizes="(min-width: 45em) 20vw, 50vw"
/>
<h4>{product.title}</h4>
<OkendoStarRating
	productId={product.id}
	okendoStarRatingSnippet={product.okendoStarRatingSnippet}
/>
<small>
	<Money data={product.priceRange.minVariantPrice} />
</small>

We now have the Okendo Star Rating widget visible on our page:

Okendo's Star Rating widget

app/routes/products.$handle.tsx

Add the following imports:

import {
	OKENDO_PRODUCT_REVIEWS_FRAGMENT,
	OKENDO_PRODUCT_STAR_RATING_FRAGMENT,
	OkendoReviews,
	OkendoStarRating,
	type WithOkendoReviewsSnippet,
	type WithOkendoStarRatingSnippet,
} from "@okendo/shopify-hydrogen";

Add the following block just before the RECOMMENDED_PRODUCTS_QUERY GraphQL query:

const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
	fragment OkendoStarRatingSnippet on Product {
		okendoStarRatingSnippet: metafield(
			namespace: "okendo"
			key: "StarRatingSnippet"
		) {
			value
		}
	}
` as const;

const OKENDO_PRODUCT_REVIEWS_FRAGMENT = `#graphql
	fragment OkendoReviewsSnippet on Product {
		okendoReviewsSnippet: metafield(
			namespace: "okendo"
			key: "ReviewsWidgetSnippet"
		) {
			value
		}
	}
` as const;

Then append ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}, ${OKENDO_PRODUCT_REVIEWS_FRAGMENT}, ...OkendoStarRatingSnippet, and ...OkendoReviewsSnippet to PRODUCT_FRAGMENT:

const PRODUCT_FRAGMENT = `#graphql
	${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
	${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
	fragment Product on Product {
		id
		title
		vendor
		handle
		descriptionHtml
		description
		options {
			name
			values
		}
		selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
			...ProductVariant
		}
		variants(first: 1) {
			nodes {
				...ProductVariant
			}
		}
		seo {
			description
			title
		}
		...OkendoStarRatingSnippet
		...OkendoReviewsSnippet
	}
	${PRODUCT_VARIANT_FRAGMENT}
` as const;

Add OkendoReviews to Product:

<OkendoReviews
	productId={product.id}
	okendoReviewsSnippet={product.okendoReviewsSnippet}
/>

For instance, we can add it below the product section, like this:

<>
	<div className="product">
		<ProductImage image={selectedVariant?.image} />
		<ProductMain
			selectedVariant={selectedVariant}
			product={product}
			variants={variants}
		/>
	</div>

	<OkendoReviews
		productId={product.id}
		okendoReviewsSnippet={product.okendoReviewsSnippet}
	/>
</>

Tweak the type of the product prop of ProductMain:

product: ProductFragment &
	WithOkendoStarRatingSnippet &
	WithOkendoReviewsSnippet;

Add OkendoStarRating to ProductMain:

<OkendoStarRating
	productId={product.id}
	okendoStarRatingSnippet={product.okendoStarRatingSnippet}
/>

For instance, we can add it below the product title, like this:

<div className="product-main">
	<h1>{title}</h1>
	<OkendoStarRating
		productId={product.id}
		okendoStarRatingSnippet={product.okendoStarRatingSnippet}
	/>
	<ProductPrice selectedVariant={selectedVariant} />

We now have the Okendo Star Rating and Reviews widgets visible on our product page:

Okendo's Star Rating and Reviews widgets

2.1.5

20 days ago

2.1.4

1 month ago

2.1.3

1 month ago

2.1.2

2 months ago

2.1.1

4 months ago

2.1.0

5 months ago

2.0.3

5 months ago

2.0.2

9 months ago

1.6.6

11 months ago

1.3.1

1 year ago

2.0.1

1 year ago

2.0.0

1 year ago

1.3.0

1 year ago

1.2.4

2 years ago

1.2.3

2 years ago

1.2.2

2 years ago

1.2.1

2 years ago

1.2.0

2 years ago

1.1.0

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago

0.0.13

2 years ago

0.0.12

2 years ago

0.0.11

2 years ago

0.0.10

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago