Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated hydrogen #95

Closed
wants to merge 14 commits into from
42 changes: 42 additions & 0 deletions spec/nosto.load.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react"
import { vi, describe, expect, it } from "vitest"
import { render } from "@testing-library/react"
import { NostoProvider, NostoHome } from "../src/index"

describe("Nosto client script loading", () => {
it("is stopped via provider when the script is loaded externally", async () => {
window.nosto = {
reload: vi.fn()
}

render(
<NostoProvider account="shopify-11368366139" loadScript={false}>
<NostoHome />
</NostoProvider>
)

expect(document.querySelector("[nosto-client-script]")).not.toBeInTheDocument()
})

it("is loaded", () => {
render(
<NostoProvider account="shopify-11368366139">
<NostoHome />
</NostoProvider>
)

expect(document.querySelector("[nosto-client-script]")).toBeInTheDocument()
})

it("Shopify markets script", () => {
render(
<NostoProvider account="shopify-11368366139" shopifyMarkets={{ language: "en", marketId: "123" }}>
<NostoHome />
</NostoProvider>
)

expect(document.querySelector("[nosto-client-script]")).toBeInTheDocument()
expect(document.querySelector("[nosto-client-script]")?.getAttribute("nosto-language")).toBe("en")
expect(document.querySelector("[nosto-client-script]")?.getAttribute("nosto-market-id")).toBe("123")
})
})
2 changes: 1 addition & 1 deletion spec/nosto.reload.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ test("verify Nosto is not loaded twice", async () => {
)

expect(document.querySelector("[nosto-client-script]")).not.toBeInTheDocument()
})
})
9 changes: 8 additions & 1 deletion spec/setup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "@testing-library/jest-dom/vitest"
import { JSDOM } from "jsdom"
import { afterEach } from "vitest"

const { window } = new JSDOM("<html></html>", {
url: "http://localhost",
Expand All @@ -12,4 +13,10 @@ global.localStorage = window.localStorage
global.navigator = window.navigator

// test mode flag
global.window.nostoReactTest = true
global.window.nostoReactTest = true

afterEach(() => {
window.nosto = undefined
document.head.innerHTML = ""
document.body.innerHTML = ""
})
85 changes: 9 additions & 76 deletions src/components/NostoProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, isValidElement } from "react"
import { isValidElement } from "react"
import type { ReactElement } from "react"
import { NostoContext, RecommendationComponent } from "../context"
import { NostoClient } from "../types"
import { isNostoLoaded } from "./helpers"
import { useClientScriptLoad } from "../hooks"

/**
* @group Components
Expand All @@ -22,7 +22,7 @@ export interface NostoProviderProps {
/**
* children
*/
children: React.ReactElement | React.ReactElement[]
children: ReactElement | ReactElement[]
/**
* Indicates if merchant uses multiple currencies
*/
Expand All @@ -38,6 +38,10 @@ export interface NostoProviderProps {
language?: string
marketId?: string | number
}
/**
* Load nosto script (should be false if loading the script outside of nosto-react)
*/
loadScript?: boolean
}

/**
Expand Down Expand Up @@ -65,88 +69,17 @@ export default function NostoProvider(props: NostoProviderProps) {
const {
account,
multiCurrency = false,
host,
children,
recommendationComponent,
shopifyMarkets
} = props
const [clientScriptLoadedState, setClientScriptLoadedState] = React.useState(false)
const clientScriptLoaded = React.useMemo(() => clientScriptLoadedState, [clientScriptLoadedState])

// Pass currentVariation as empty string if multiCurrency is disabled
const currentVariation = multiCurrency ? props.currentVariation : ""

// Set responseMode for loading campaigns:
const responseMode = isValidElement(recommendationComponent) ? "JSON_ORIGINAL" : "HTML"

useEffect(() => {
if (!window.nostojs) {
window.nostojs = (cb: (api: NostoClient) => void) => {
(window.nostojs.q = window.nostojs.q || []).push(cb)
}
window.nostojs(api => api.setAutoLoad(false))
}

if (!isNostoLoaded() && !shopifyMarkets) {
const script = document.createElement("script")
script.type = "text/javascript"
script.src = "//" + (host || "connect.nosto.com") + "/include/" + account
script.async = true
script.setAttribute("nosto-client-script", "")

script.onload = () => {
if ("nostoReactTest" in window) {
window.nosto?.reload({
site: "localhost",
})
}
setClientScriptLoadedState(true)
}
document.body.appendChild(script)
}

// Enable Shopify markets functionality:
if (shopifyMarkets) {
const existingScript = document.querySelector("[nosto-client-script]")
const nostoSandbox = document.querySelector("#nosto-sandbox")

if (
!existingScript ||
existingScript?.getAttribute("nosto-language") !== shopifyMarkets?.language ||
existingScript?.getAttribute("nosto-market-id") !== shopifyMarkets?.marketId
) {
if (clientScriptLoadedState) {
setClientScriptLoadedState(false)
}

existingScript?.parentNode?.removeChild(existingScript)
nostoSandbox?.parentNode?.removeChild(nostoSandbox)

const script = document.createElement("script")
script.type = "text/javascript"
script.src =
"//" +
(host || "connect.nosto.com") +
`/script/shopify/market/nosto.js?merchant=${account}&market=${
shopifyMarkets.marketId || ""
}&locale=${shopifyMarkets?.language?.toLowerCase() || ""}`
script.async = true
script.setAttribute("nosto-client-script", "")
script.setAttribute("nosto-language", shopifyMarkets?.language || "")
script.setAttribute("nosto-market-id", String(shopifyMarkets?.marketId))

script.onload = () => {
if ("nostoReactTest" in window) {
window.nosto?.reload({
site: "localhost",
})
}
setClientScriptLoadedState(true)
}
document.body.appendChild(script)
}
}
}, [clientScriptLoadedState, shopifyMarkets])
const { clientScriptLoaded } = useClientScriptLoad(props)

return (
<NostoContext.Provider
Expand Down
3 changes: 2 additions & 1 deletion src/components/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export function isNostoLoaded() {
return typeof window.nosto !== "undefined"
}
}

3 changes: 2 additions & 1 deletion src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { useDeepCompareEffect } from "./useDeepCompareEffect"
export { useNostoApi } from "./useNostoApi"
export { useNostoContext } from "./useNostoContext"
export { useRenderCampaigns } from "./useRenderCampaigns"
export { useRenderCampaigns } from "./useRenderCampaigns"
export { useClientScriptLoad } from "./useClientScriptLoad"
87 changes: 87 additions & 0 deletions src/hooks/useClientScriptLoad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useState, useMemo, useEffect } from "react"
import { isNostoLoaded } from "../components/helpers"
import type { NostoClient } from "../types"
import type { NostoProviderProps } from "../components/NostoProvider"

type NostoScriptProps = Pick<NostoProviderProps, "account" | "host" | "shopifyMarkets" | "loadScript">

export function useClientScriptLoad(props: NostoScriptProps) {
const { host, account, shopifyMarkets, loadScript = true } = props
const [clientScriptLoadedState, setClientScriptLoadedState] = useState(false)
const clientScriptLoaded = useMemo(() => clientScriptLoadedState, [clientScriptLoadedState])

useEffect(() => {

const scriptOnload = () => {
if ("nostoReactTest" in window) {
window.nosto?.reload({
site: "localhost"
})
}
setClientScriptLoadedState(true)
}

function createScriptElement(urlPartial: string) {
const scriptEl = document.createElement("script")
scriptEl.type = "text/javascript"
scriptEl.src = `//${(host || "connect.nosto.com")}${urlPartial}`
scriptEl.async = true
scriptEl.setAttribute("nosto-client-script", "")
return scriptEl
}

if (!window.nostojs) {
window.nostojs = (cb: (api: NostoClient) => void) => {
(window.nostojs.q = window.nostojs.q || []).push(cb)
}
window.nostojs(api => api.setAutoLoad(false))
}

if (!isNostoLoaded() && !shopifyMarkets && loadScript) {
const clientScriptURLPartial = `/include/${account}`
const script = createScriptElement(clientScriptURLPartial)
script.onload = scriptOnload
document.body.appendChild(script)
}

// Load Shopify Markets scripts
if (shopifyMarkets && loadScript) {
const existingScript = document.querySelector("[nosto-client-script]")
const nostoSandbox = document.querySelector("#nosto-sandbox")

if (
!existingScript ||
existingScript?.getAttribute("nosto-language") !== shopifyMarkets?.language ||
existingScript?.getAttribute("nosto-market-id") !== shopifyMarkets?.marketId
) {
if (clientScriptLoadedState) {
setClientScriptLoadedState(false)
}

existingScript?.parentNode?.removeChild(existingScript)
nostoSandbox?.parentNode?.removeChild(nostoSandbox)

const urlPartial =
"//" +
(host || "connect.nosto.com") +
`/script/shopify/market/nosto.js?merchant=${account}&market=${shopifyMarkets.marketId || ""
}&locale=${shopifyMarkets?.language?.toLowerCase() || ""}`
const script = createScriptElement(urlPartial)
script.setAttribute("nosto-language", shopifyMarkets?.language || "")
script.setAttribute("nosto-market-id", String(shopifyMarkets?.marketId))
script.onload = scriptOnload
document.body.appendChild(script)
}
}

if (!loadScript) {
window.nosto?.reload({
site: "localhost",
})
setClientScriptLoadedState(true)
}

}, [clientScriptLoadedState, shopifyMarkets])

return { clientScriptLoaded }
}
Loading