Skip to content

Commit

Permalink
ShopPayButton now accepts storeDomain prop (#645)
Browse files Browse the repository at this point in the history
* extract shopify default context

* update ShopPayButton to also accept a storeDomain prop

Co-authored-by: Juan P. Prieto <juanpablo.prieto@shopify.com>

* fix shop pay button in demo store

* add changeset

* bust turbo cache

* Revert "bust turbo cache"

This reverts commit 6023e83.

* Update docs

* fix test and generate doc

* fix name

* remove turbo cache

* revert tubro cache

---------

Co-authored-by: Juan P. Prieto <juanpablo.prieto@shopify.com>
Co-authored-by: Helen Lin <helen.lin@shopify.com>
  • Loading branch information
3 people committed Mar 9, 2023
1 parent db9d44d commit c78f441
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/flat-lions-yell.md
@@ -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
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
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
@@ -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
@@ -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
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
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
@@ -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
@@ -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
@@ -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]} />;
}

0 comments on commit c78f441

Please sign in to comment.