Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/flat-lions-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen-react': patch
---

`ShopPayButton` component now can receive a `storeDomain`. The component now does not require `ShopifyProvider`.
52 changes: 46 additions & 6 deletions packages/hydrogen-react/docs/generated/generated_docs_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -4683,26 +4683,49 @@
"category": "components",
"isVisualComponent": false,
"related": [],
"description": "The `ShopPayButton` component renders a button that redirects to the Shop Pay checkout. It renders a [`<shop-pay-button>`](https://shopify.dev/custom-storefronts/tools/web-components) custom element, for which it will lazy-load the source code automatically. It relies on the `<ShopProvider>` context provider.",
"description": "The `ShopPayButton` component renders a button that redirects to the Shop Pay checkout. It renders a [`<shop-pay-button>`](https://shopify.dev/custom-storefronts/tools/web-components) custom element, for which it will lazy-load the source code automatically.",
"type": "component",
"defaultExample": {
"description": "I am the default example",
"description": "<ShopPayButton> without <ShopifyProvider>",
"codeblock": {
"tabs": [
{
"title": "JavaScript",
"code": "import {ShopPayButton} from '@shopify/hydrogen-react';\n\nexport function AddVariantQuantity1({variantId}) {\n return <ShopPayButton variantIds={[variantId]} />;\n}\n\nexport function AddVariantQuantityMultiple({variantId, quantity}) {\n return (\n <ShopPayButton variantIdsAndQuantities={[{id: variantId, quantity}]} />\n );\n}\n",
"code": "import {ShopPayButton} from '@shopify/hydrogen-react';\n\nexport function AddVariantQuantity1({variantId, storeDomain}) {\n return <ShopPayButton variantIds={[variantId]} storeDomain={storeDomain} />;\n}\n\nexport function AddVariantQuantityMultiple({variantId, quantity, storeDomain}) {\n return (\n <ShopPayButton\n variantIdsAndQuantities={[{id: variantId, quantity}]}\n storeDomain={storeDomain}\n />\n );\n}\n",
"language": "jsx"
},
{
"title": "TypeScript",
"code": "import {ShopPayButton} from '@shopify/hydrogen-react';\n\nexport function AddVariantQuantity1({variantId}: {variantId: string}) {\n return <ShopPayButton variantIds={[variantId]} />;\n}\n\nexport function AddVariantQuantityMultiple({\n variantId,\n quantity,\n}: {\n variantId: string;\n quantity: number;\n}) {\n return (\n <ShopPayButton variantIdsAndQuantities={[{id: variantId, quantity}]} />\n );\n}\n",
"code": "import {ShopPayButton} from '@shopify/hydrogen-react';\n\nexport function AddVariantQuantity1({\n variantId,\n storeDomain,\n}: {\n variantId: string;\n storeDomain: string;\n}) {\n return <ShopPayButton variantIds={[variantId]} storeDomain={storeDomain} />;\n}\n\nexport function AddVariantQuantityMultiple({\n variantId,\n quantity,\n storeDomain,\n}: {\n variantId: string;\n quantity: number;\n storeDomain: string;\n}) {\n return (\n <ShopPayButton\n variantIdsAndQuantities={[{id: variantId, quantity}]}\n storeDomain={storeDomain}\n />\n );\n}\n",
"language": "tsx"
}
],
"title": "Example code"
"title": "<ShopPayButton> without <ShopifyProvider>"
}
},
"examples": {
"description": "",
"examples": [
{
"description": "If `<ShopifyProvider>` context provider is used in your app, you can use the `<ShopPayButton>` without supplying a `storeDomain` prop",
"codeblock": {
"tabs": [
{
"title": "JavaScript",
"code": "import {ShopifyProvider, ShopPayButton} from '@shopify/hydrogen-react';\n\nexport default function App() {\n return (\n <ShopifyProvider\n storeDomain=\"my-store\"\n storefrontToken=\"abc123\"\n storefrontApiVersion=\"2023-01\"\n countryIsoCode=\"CA\"\n languageIsoCode=\"EN\"\n >\n <AddVariantQuantity1 variantId=\"gid://shopify/ProductVariant/1\" />\n </ShopifyProvider>\n );\n}\n\nexport function AddVariantQuantity1({variantId}) {\n return <ShopPayButton variantIds={[variantId]} />;\n}\n",
"language": "jsx"
},
{
"title": "TypeScript",
"code": "import {ShopifyProvider, ShopPayButton} from '@shopify/hydrogen-react';\n\nexport default function App() {\n return (\n <ShopifyProvider\n storeDomain=\"my-store\"\n storefrontToken=\"abc123\"\n storefrontApiVersion=\"2023-01\"\n countryIsoCode=\"CA\"\n languageIsoCode=\"EN\"\n >\n <AddVariantQuantity1 variantId=\"gid://shopify/ProductVariant/1\" />\n </ShopifyProvider>\n );\n}\n\nexport function AddVariantQuantity1({variantId}: {variantId: string}) {\n return <ShopPayButton variantIds={[variantId]} />;\n}\n",
"language": "tsx"
}
],
"title": "<ShopPayButton> with <ShopifyProvider>"
}
}
]
},
"definitions": [
{
"title": "Props",
Expand All @@ -4713,7 +4736,7 @@
"filePath": "/ShopPayButton.tsx",
"syntaxKind": "TypeAliasDeclaration",
"name": "ShopPayButtonProps",
"value": "ShopPayButtonStyleProps & (ShopPayVariantIds | ShopPayVariantAndQuantities)",
"value": "ShopPayButtonStyleProps & ShopPayDomainProps & (ShopPayVariantIds | ShopPayVariantAndQuantities)",
"description": ""
},
"ShopPayButtonStyleProps": {
Expand Down Expand Up @@ -4741,6 +4764,23 @@
}
]
},
"ShopPayDomainProps": {
"filePath": "/ShopPayButton.tsx",
"syntaxKind": "TypeAliasDeclaration",
"name": "ShopPayDomainProps",
"value": "{\n /** The domain of your Shopify storefront URL (eg: `your-store.myshopify.com`). */\n storeDomain?: string;\n}",
"description": "",
"members": [
{
"filePath": "/ShopPayButton.tsx",
"syntaxKind": "PropertySignature",
"name": "storeDomain",
"value": "string",
"description": "The domain of your Shopify storefront URL (eg: `your-store.myshopify.com`).",
"isOptional": true
}
]
},
"ShopPayVariantIds": {
"filePath": "/ShopPayButton.tsx",
"syntaxKind": "TypeAliasDeclaration",
Expand Down
30 changes: 27 additions & 3 deletions packages/hydrogen-react/src/ShopPayButton.doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const data: ReferenceEntityTemplateSchema = {
category: 'components',
isVisualComponent: false,
related: [],
description: `The \`ShopPayButton\` component renders a button that redirects to the Shop Pay checkout. It renders a [\`<shop-pay-button>\`](https://shopify.dev/custom-storefronts/tools/web-components) custom element, for which it will lazy-load the source code automatically. It relies on the \`<ShopProvider>\` context provider.`,
description: `The \`ShopPayButton\` component renders a button that redirects to the Shop Pay checkout. It renders a [\`<shop-pay-button>\`](https://shopify.dev/custom-storefronts/tools/web-components) custom element, for which it will lazy-load the source code automatically.`,
type: 'component',
defaultExample: {
description: 'I am the default example',
description: '<ShopPayButton> without <ShopifyProvider>',
codeblock: {
tabs: [
{
Expand All @@ -22,9 +22,33 @@ const data: ReferenceEntityTemplateSchema = {
language: 'tsx',
},
],
title: 'Example code',
title: '<ShopPayButton> without <ShopifyProvider>',
},
},
examples: {
description: '',
examples: [
{
description:
'If `<ShopifyProvider>` context provider is used in your app, you can use the `<ShopPayButton>` without supplying a `storeDomain` prop',
codeblock: {
tabs: [
{
title: 'JavaScript',
code: './ShopPayButton2.example.jsx',
language: 'jsx',
},
{
title: 'TypeScript',
code: './ShopPayButton2.example.tsx',
language: 'tsx',
},
],
title: '<ShopPayButton> with <ShopifyProvider>',
},
},
],
},
definitions: [
{
title: 'Props',
Expand Down
11 changes: 7 additions & 4 deletions packages/hydrogen-react/src/ShopPayButton.example.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {ShopPayButton} from '@shopify/hydrogen-react';

export function AddVariantQuantity1({variantId}) {
return <ShopPayButton variantIds={[variantId]} />;
export function AddVariantQuantity1({variantId, storeDomain}) {
return <ShopPayButton variantIds={[variantId]} storeDomain={storeDomain} />;
}

export function AddVariantQuantityMultiple({variantId, quantity}) {
export function AddVariantQuantityMultiple({variantId, quantity, storeDomain}) {
return (
<ShopPayButton variantIdsAndQuantities={[{id: variantId, quantity}]} />
<ShopPayButton
variantIdsAndQuantities={[{id: variantId, quantity}]}
storeDomain={storeDomain}
/>
);
}
17 changes: 14 additions & 3 deletions packages/hydrogen-react/src/ShopPayButton.example.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import {ShopPayButton} from '@shopify/hydrogen-react';

export function AddVariantQuantity1({variantId}: {variantId: string}) {
return <ShopPayButton variantIds={[variantId]} />;
export function AddVariantQuantity1({
variantId,
storeDomain,
}: {
variantId: string;
storeDomain: string;
}) {
return <ShopPayButton variantIds={[variantId]} storeDomain={storeDomain} />;
}

export function AddVariantQuantityMultiple({
variantId,
quantity,
storeDomain,
}: {
variantId: string;
quantity: number;
storeDomain: string;
}) {
return (
<ShopPayButton variantIdsAndQuantities={[{id: variantId, quantity}]} />
<ShopPayButton
variantIdsAndQuantities={[{id: variantId, quantity}]}
storeDomain={storeDomain}
/>
);
}
11 changes: 9 additions & 2 deletions packages/hydrogen-react/src/ShopPayButton.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@ const Template: Story<ButtonProps> = (props) => <ShopPayButton {...props} />;

export const NoQuantity = Template.bind({});
NoQuantity.args = {
variantIds: ['123', '456'],
variantIds: [
'gid://shopify/ProductVariant/123',
'gid://shopify/ProductVariant/456',
],
storeDomain: 'https://notashop.myshopify.io',
className: '',
width: '',
};

export const Quantities = Template.bind({});
Quantities.args = {
variantIdsAndQuantities: [{id: '123', quantity: 2}],
variantIdsAndQuantities: [
{id: 'gid://shopify/ProductVariant/123', quantity: 2},
],
storeDomain: 'https://notashop.myshopify.io',
className: '',
width: '',
};
50 changes: 50 additions & 0 deletions packages/hydrogen-react/src/ShopPayButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
DoublePropsErrorMessage,
MissingPropsErrorMessage,
InvalidPropsErrorMessage,
MissingStoreDomainErrorMessage,
} from './ShopPayButton.js';
import {getShopifyConfig} from './ShopifyProvider.test.js';

Expand Down Expand Up @@ -114,4 +115,53 @@ describe(`<ShopPayButton />`, () => {
}),
).toThrow(InvalidPropsErrorMessage);
});

it(`throws error if no 'storeDomain' is supplied`, () => {
const fakeId = 'gid://shopify/ProductVariant/123';
expect(() => render(<ShopPayButton variantIds={[fakeId]} />)).toThrow(
MissingStoreDomainErrorMessage,
);
});

it(`allows to use 'storeDomain' props without ShopifyProvider`, () => {
const fakeId = 'gid://shopify/ProductVariant/123';
const {container} = render(
<ShopPayButton
variantIds={[fakeId]}
storeDomain="https://notashop.myshopify.com"
/>,
);
const button = container.querySelector('shop-pay-button');

expect(button).toHaveAttribute(
'store-url',
'https://notashop.myshopify.com',
);
});

it(`uses 'storeDomain' props over 'ShopifyProvider'`, () => {
const fakeId = 'gid://shopify/ProductVariant/123';
const {container} = render(
<ShopPayButton
variantIds={[fakeId]}
storeDomain="https://notashop.myshopify.com"
/>,
{
wrapper: ({children}) => (
<ShopifyProvider
{...getShopifyConfig()}
storeDomain="https://diffshop.myshopify.com"
>
{children}
</ShopifyProvider>
),
},
);
const button = container.querySelector('shop-pay-button');

expect(button).toHaveAttribute(
'store-url',
'https://notashop.myshopify.com',
);
});
});
22 changes: 20 additions & 2 deletions packages/hydrogen-react/src/ShopPayButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {useShop} from './ShopifyProvider.js';
import {defaultShopifyContext, useShop} from './ShopifyProvider.js';
import {useLoadScript} from './load-script.js';
import {parseGid} from './analytics-utils.js';

// By using 'never' in the "or" cases below, it makes these props "exclusive" and means that you cannot pass both of them; you must pass either one OR the other.
type ShopPayButtonProps = ShopPayButtonStyleProps &
ShopPayDomainProps &
(ShopPayVariantIds | ShopPayVariantAndQuantities);

type ShopPayButtonStyleProps = {
Expand All @@ -13,6 +14,11 @@ type ShopPayButtonStyleProps = {
width?: string;
};

type ShopPayDomainProps = {
/** The domain of your Shopify storefront URL (eg: `your-store.myshopify.com`). */
storeDomain?: string;
};

type ShopPayVariantIds = {
/** An array of IDs of the variants to purchase with Shop Pay. This will only ever have a quantity of 1 for each variant. If you want to use other quantities, then use `variantIdsAndQuantities`. */
variantIds: string[];
Expand Down Expand Up @@ -55,16 +61,26 @@ export function ShopPayButton({
className,
variantIdsAndQuantities,
width,
storeDomain: _storeDomain,
}: ShopPayButtonProps): JSX.Element {
const {storeDomain} = useShop();
const shop = useShop();
const storeDomain = _storeDomain || shop?.storeDomain;
const shopPayLoadedStatus = useLoadScript(SHOPJS_URL);

let ids: string[] = [];

if (!storeDomain || storeDomain === defaultShopifyContext.storeDomain) {
throw new Error(MissingStoreDomainErrorMessage);
}

if (variantIds && variantIdsAndQuantities) {
throw new Error(DoublePropsErrorMessage);
}

if (!variantIds && !variantIdsAndQuantities) {
throw new Error(MissingPropsErrorMessage);
}

if (variantIds) {
ids = variantIds.reduce<string[]>((prev, curr) => {
const bareId = parseGid(curr).id;
Expand Down Expand Up @@ -104,6 +120,8 @@ export function ShopPayButton({
);
}

export const MissingStoreDomainErrorMessage =
'You must pass a "storeDomain" prop to the "ShopPayButton" component, or wrap it in a "ShopifyProvider" component.';
export const InvalidPropsErrorMessage = `You must pass in "variantIds" in the form of ["gid://shopify/ProductVariant/1"]`;
export const MissingPropsErrorMessage = `You must pass in either "variantIds" or "variantIdsAndQuantities" to ShopPayButton`;
export const DoublePropsErrorMessage = `You must provide either a variantIds or variantIdsAndQuantities prop, but not both in the ShopPayButton component`;
19 changes: 19 additions & 0 deletions packages/hydrogen-react/src/ShopPayButton2.example.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {ShopifyProvider, ShopPayButton} from '@shopify/hydrogen-react';

export default function App() {
return (
<ShopifyProvider
storeDomain="my-store"
storefrontToken="abc123"
storefrontApiVersion="2023-01"
countryIsoCode="CA"
languageIsoCode="EN"
>
<AddVariantQuantity1 variantId="gid://shopify/ProductVariant/1" />
</ShopifyProvider>
);
}

export function AddVariantQuantity1({variantId}) {
return <ShopPayButton variantIds={[variantId]} />;
}
19 changes: 19 additions & 0 deletions packages/hydrogen-react/src/ShopPayButton2.example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {ShopifyProvider, ShopPayButton} from '@shopify/hydrogen-react';

export default function App() {
return (
<ShopifyProvider
storeDomain="my-store"
storefrontToken="abc123"
storefrontApiVersion="2023-01"
countryIsoCode="CA"
languageIsoCode="EN"
>
<AddVariantQuantity1 variantId="gid://shopify/ProductVariant/1" />
</ShopifyProvider>
);
}

export function AddVariantQuantity1({variantId}: {variantId: string}) {
return <ShopPayButton variantIds={[variantId]} />;
}
Loading