diff --git a/.changeset/strong-swans-melt.md b/.changeset/strong-swans-melt.md new file mode 100644 index 0000000000..981b341c09 --- /dev/null +++ b/.changeset/strong-swans-melt.md @@ -0,0 +1,5 @@ +--- +'@shopify/storefront-kit-react': patch +--- + +Added the `` and `` components, which have been a part of this package for awhile but weren't actually able to be used/imported. diff --git a/README.md b/README.md index d534f0a7aa..a8db3d5091 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # React Storefront Kit -React Storefront Kit is an unopionated and performant library of Shopify-specific commerce components, hooks, and utilities. +React Storefront Kit is an unopionated and performant library of Shopify-specific commerce components, hooks, and utilities. It accelerates the development of Shopify-powered custom storefronts by providing business logic, data processing, and state management on top of the [Storefront API](https://shopify.dev/api/storefront). diff --git a/packages/react/docs/generated/generated_docs_data.json b/packages/react/docs/generated/generated_docs_data.json index cdd35d1dcc..8cc62a7a6c 100644 --- a/packages/react/docs/generated/generated_docs_data.json +++ b/packages/react/docs/generated/generated_docs_data.json @@ -270,6 +270,255 @@ } ] }, + { + "name": "CartCheckoutButton", + "category": "components", + "isVisualComponent": false, + "related": [], + "description": "The `CartCheckoutButton` component renders a button that redirects to the checkout URL for the cart.\n Must be a descendent of a `CartProvider` component.\n ", + "type": "component", + "defaultExample": { + "description": "I am the default example", + "codeblock": { + "tabs": [ + { + "title": "JavaScript", + "code": "import {CartCheckoutButton} from '@shopify/storefront-kit-react';\n\nexport default function ProductCartCheckoutButton() {\n return ;\n}\n", + "language": "jsx" + }, + { + "title": "TypeScript", + "code": "import {CartCheckoutButton} from '@shopify/storefront-kit-react';\n\nexport default function ProductCartCheckoutButton() {\n return ;\n}\n", + "language": "tsx" + } + ], + "title": "Example code" + } + }, + "definitions": [ + { + "title": "Props", + "description": "", + "type": "CartCheckoutButtonProps", + "typeDefinitions": { + "CartCheckoutButtonProps": { + "filePath": "/CartCheckoutButton.tsx", + "syntaxKind": "TypeAliasDeclaration", + "name": "CartCheckoutButtonProps", + "value": "Omit, 'onClick'> & ChildrenProps", + "description": "" + }, + "BaseButtonProps": { + "filePath": "/BaseButton.tsx", + "syntaxKind": "TypeAliasDeclaration", + "name": "BaseButtonProps", + "value": "CustomBaseButtonProps & Omit<\n React.ComponentPropsWithoutRef,\n keyof CustomBaseButtonProps\n >", + "description": "" + }, + "CustomBaseButtonProps": { + "filePath": "/BaseButton.tsx", + "name": "CustomBaseButtonProps", + "description": "", + "members": [ + { + "filePath": "/BaseButton.tsx", + "syntaxKind": "PropertySignature", + "name": "as", + "value": "AsType", + "description": "Provide a React element or component to render as the underlying button. Note: for accessibility compliance, almost always you should use a `button` element, or a component that renders an underlying button.", + "isOptional": true + }, + { + "filePath": "/BaseButton.tsx", + "syntaxKind": "PropertySignature", + "name": "children", + "value": "ReactNode", + "description": "Any ReactNode elements." + }, + { + "filePath": "/BaseButton.tsx", + "syntaxKind": "PropertySignature", + "name": "onClick", + "value": "(event?: MouseEvent) => boolean | void", + "description": "Click event handler. Default behaviour triggers unless prevented", + "isOptional": true + }, + { + "filePath": "/BaseButton.tsx", + "syntaxKind": "PropertySignature", + "name": "defaultOnClick", + "value": "(event?: MouseEvent) => boolean | void", + "description": "A default onClick behavior", + "isOptional": true + }, + { + "filePath": "/BaseButton.tsx", + "syntaxKind": "PropertySignature", + "name": "buttonRef", + "value": "Ref", + "description": "A ref to the underlying button", + "isOptional": true + } + ], + "value": "export interface CustomBaseButtonProps {\n /** Provide a React element or component to render as the underlying button. Note: for accessibility compliance, almost always you should use a `button` element, or a component that renders an underlying button. */\n as?: AsType;\n /** Any ReactNode elements. */\n children: ReactNode;\n /** Click event handler. Default behaviour triggers unless prevented */\n onClick?: (\n event?: React.MouseEvent\n ) => void | boolean;\n /** A default onClick behavior */\n defaultOnClick?: (\n event?: React.MouseEvent\n ) => void | boolean;\n /** A ref to the underlying button */\n buttonRef?: Ref;\n}" + }, + "ChildrenProps": { + "filePath": "/CartCheckoutButton.tsx", + "syntaxKind": "TypeAliasDeclaration", + "name": "ChildrenProps", + "value": "{\n /** A `ReactNode` element. */\n children: ReactNode;\n}", + "description": "", + "members": [ + { + "filePath": "/CartCheckoutButton.tsx", + "syntaxKind": "PropertySignature", + "name": "children", + "value": "ReactNode", + "description": "A `ReactNode` element." + } + ] + } + } + } + ] + }, + { + "name": "CartCost", + "category": "components", + "isVisualComponent": false, + "related": [], + "description": "\n The `CartCost` component renders a `Money` component with the cost associated with the `amountType` prop. \n If no `amountType` prop is specified, then it defaults to `totalAmount`.\n Depends on `useCart()` and must be a child of ``\n ", + "type": "component", + "defaultExample": { + "description": "I am the default example", + "codeblock": { + "tabs": [ + { + "title": "JavaScript", + "code": "import {CartCost} from '@shopify/storefront-kit-react';\n\nexport default function CartTotals() {\n return (\n <>\n
\n Subtotal: \n
\n
\n Tax: \n
\n
\n Total: \n
\n \n );\n}\n", + "language": "jsx" + }, + { + "title": "TypeScript", + "code": "import {CartCost} from '@shopify/storefront-kit-react';\n\nexport default function CartTotals() {\n return (\n <>\n
\n Subtotal: \n
\n
\n Tax: \n
\n
\n Total: \n
\n \n );\n}\n", + "language": "tsx" + } + ], + "title": "Example code" + } + }, + "definitions": [ + { + "title": "Props", + "description": "", + "type": "CartCostProps", + "typeDefinitions": { + "CartCostProps": { + "filePath": "/CartCost.tsx", + "syntaxKind": "TypeAliasDeclaration", + "name": "CartCostProps", + "value": "Omit, 'data'> & CartCostPropsBase", + "description": "" + }, + "CartCostPropsBase": { + "filePath": "/CartCost.tsx", + "name": "CartCostPropsBase", + "description": "", + "members": [ + { + "filePath": "/CartCost.tsx", + "syntaxKind": "PropertySignature", + "name": "amountType", + "value": "\"total\" | \"subtotal\" | \"tax\" | \"duty\"", + "description": "A string type that defines the type of cost needed. Valid values: `total`, `subtotal`, `tax`, or `duty`.", + "isOptional": true + }, + { + "filePath": "/CartCost.tsx", + "syntaxKind": "PropertySignature", + "name": "children", + "value": "ReactNode", + "description": "Any `ReactNode` elements.", + "isOptional": true + } + ], + "value": "interface CartCostPropsBase {\n /** A string type that defines the type of cost needed. Valid values: `total`, `subtotal`, `tax`, or `duty`. */\n amountType?: 'total' | 'subtotal' | 'tax' | 'duty';\n /** Any `ReactNode` elements. */\n children?: React.ReactNode;\n}" + } + } + } + ] + }, + { + "name": "CartLinePrice", + "category": "components", + "isVisualComponent": false, + "related": [ + { + "name": "Money", + "type": "component", + "url": "/api/react-storefront-kit/components/money" + } + ], + "description": "\n The `CartLinePrice` component renders a `Money` component for the cart line merchandise's price or compare at price.\n ", + "type": "component", + "defaultExample": { + "description": "I am the default example", + "codeblock": { + "tabs": [ + { + "title": "JavaScript", + "code": "import {CartLinePrice} from '@shopify/storefront-kit-react';\n\nexport default function ProductCartLinePrice({cartLine}) {\n return ;\n}\n", + "language": "jsx" + }, + { + "title": "TypeScript", + "code": "import {CartLinePrice} from '@shopify/storefront-kit-react';\nimport type {CartLine} from '@shopify/storefront-kit-react/storefront-api-types';\n\nexport default function ProductCartLinePrice({cartLine}: {cartLine: CartLine}) {\n return ;\n}\n", + "language": "tsx" + } + ], + "title": "Example code" + } + }, + "definitions": [ + { + "title": "Props", + "description": "", + "type": "CartLinePriceProps", + "typeDefinitions": { + "CartLinePriceProps": { + "filePath": "/CartLinePrice.tsx", + "syntaxKind": "TypeAliasDeclaration", + "name": "CartLinePriceProps", + "value": "Omit, 'data'> & CartLinePricePropsBase", + "description": "" + }, + "CartLinePricePropsBase": { + "filePath": "/CartLinePrice.tsx", + "name": "CartLinePricePropsBase", + "description": "", + "members": [ + { + "filePath": "/CartLinePrice.tsx", + "syntaxKind": "PropertySignature", + "name": "data", + "value": "PartialObjectDeep", + "description": "A [CartLine object](https://shopify.dev/api/storefront/reference/objects/CartLine)." + }, + { + "filePath": "/CartLinePrice.tsx", + "syntaxKind": "PropertySignature", + "name": "priceType", + "value": "\"regular\" | \"compareAt\"", + "description": "The type of price. Valid values:`regular` (default) or `compareAt`.", + "isOptional": true + } + ], + "value": "interface CartLinePricePropsBase {\n /** A [CartLine object](https://shopify.dev/api/storefront/reference/objects/CartLine). */\n data: PartialDeep;\n /** The type of price. Valid values:`regular` (default) or `compareAt`. */\n priceType?: 'regular' | 'compareAt';\n}" + } + } + } + ] + }, { "name": "ExternalVideo", "category": "components", diff --git a/packages/react/src/CartCheckoutButton.doc.ts b/packages/react/src/CartCheckoutButton.doc.ts new file mode 100644 index 0000000000..97018020fc --- /dev/null +++ b/packages/react/src/CartCheckoutButton.doc.ts @@ -0,0 +1,39 @@ +import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'CartCheckoutButton', + category: 'components', + isVisualComponent: false, + related: [], + description: `The \`CartCheckoutButton\` component renders a button that redirects to the checkout URL for the cart. + Must be a descendent of a \`CartProvider\` component. + `, + type: 'component', + defaultExample: { + description: 'I am the default example', + codeblock: { + tabs: [ + { + title: 'JavaScript', + code: './CartCheckoutButton.example.jsx', + language: 'jsx', + }, + { + title: 'TypeScript', + code: './CartCheckoutButton.example.tsx', + language: 'tsx', + }, + ], + title: 'Example code', + }, + }, + definitions: [ + { + title: 'Props', + type: 'CartCheckoutButtonProps', + description: '', + }, + ], +}; + +export default data; diff --git a/packages/react/src/CartCheckoutButton.example.jsx b/packages/react/src/CartCheckoutButton.example.jsx new file mode 100644 index 0000000000..f5abdc91be --- /dev/null +++ b/packages/react/src/CartCheckoutButton.example.jsx @@ -0,0 +1,5 @@ +import {CartCheckoutButton} from '@shopify/storefront-kit-react'; + +export default function ProductCartCheckoutButton() { + return ; +} diff --git a/packages/react/src/CartCheckoutButton.example.tsx b/packages/react/src/CartCheckoutButton.example.tsx new file mode 100644 index 0000000000..f5abdc91be --- /dev/null +++ b/packages/react/src/CartCheckoutButton.example.tsx @@ -0,0 +1,5 @@ +import {CartCheckoutButton} from '@shopify/storefront-kit-react'; + +export default function ProductCartCheckoutButton() { + return ; +} diff --git a/packages/react/src/CartCheckoutButton.tsx b/packages/react/src/CartCheckoutButton.tsx index 2462d83a8d..58a97a66a3 100644 --- a/packages/react/src/CartCheckoutButton.tsx +++ b/packages/react/src/CartCheckoutButton.tsx @@ -2,18 +2,18 @@ import {ReactNode, useEffect, useState} from 'react'; import {useCart} from './CartProvider.js'; import {BaseButton, BaseButtonProps} from './BaseButton.js'; -type PropsWeControl = 'onClick'; +type ChildrenProps = { + /** A `ReactNode` element. */ + children: ReactNode; +}; +type CartCheckoutButtonProps = Omit, 'onClick'> & + ChildrenProps; /** * The `CartCheckoutButton` component renders a button that redirects to the checkout URL for the cart. * It must be a descendent of a `CartProvider` component. */ -export function CartCheckoutButton( - props: Omit, PropsWeControl> & { - /** A `ReactNode` element. */ - children: ReactNode; - } -) { +export function CartCheckoutButton(props: CartCheckoutButtonProps) { const [requestedCheckout, setRequestedCheckout] = useState(false); const {status, checkoutUrl} = useCart(); const {children, ...passthroughProps} = props; diff --git a/packages/react/src/CartCost.doc.ts b/packages/react/src/CartCost.doc.ts new file mode 100644 index 0000000000..c6a5a8c936 --- /dev/null +++ b/packages/react/src/CartCost.doc.ts @@ -0,0 +1,41 @@ +import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'CartCost', + category: 'components', + isVisualComponent: false, + related: [], + description: ` + The \`CartCost\` component renders a \`Money\` component with the cost associated with the \`amountType\` prop. + If no \`amountType\` prop is specified, then it defaults to \`totalAmount\`. + Depends on \`useCart()\` and must be a child of \`\` + `, + type: 'component', + defaultExample: { + description: 'I am the default example', + codeblock: { + tabs: [ + { + title: 'JavaScript', + code: './CartCost.example.jsx', + language: 'jsx', + }, + { + title: 'TypeScript', + code: './CartCost.example.tsx', + language: 'tsx', + }, + ], + title: 'Example code', + }, + }, + definitions: [ + { + title: 'Props', + type: 'CartCostProps', + description: '', + }, + ], +}; + +export default data; diff --git a/packages/react/src/CartCost.example.jsx b/packages/react/src/CartCost.example.jsx new file mode 100644 index 0000000000..ecf99333b5 --- /dev/null +++ b/packages/react/src/CartCost.example.jsx @@ -0,0 +1,17 @@ +import {CartCost} from '@shopify/storefront-kit-react'; + +export default function CartTotals() { + return ( + <> +
+ Subtotal: +
+
+ Tax: +
+
+ Total: +
+ + ); +} diff --git a/packages/react/src/CartCost.example.tsx b/packages/react/src/CartCost.example.tsx new file mode 100644 index 0000000000..ecf99333b5 --- /dev/null +++ b/packages/react/src/CartCost.example.tsx @@ -0,0 +1,17 @@ +import {CartCost} from '@shopify/storefront-kit-react'; + +export default function CartTotals() { + return ( + <> +
+ Subtotal: +
+
+ Tax: +
+
+ Total: +
+ + ); +} diff --git a/packages/react/src/CartCost.tsx b/packages/react/src/CartCost.tsx index 9fd40f3372..e4cec181d6 100644 --- a/packages/react/src/CartCost.tsx +++ b/packages/react/src/CartCost.tsx @@ -1,21 +1,22 @@ import {Money} from './Money.js'; import {useCart} from './CartProvider.js'; -export interface CartCostProps { +interface CartCostPropsBase { /** A string type that defines the type of cost needed. Valid values: `total`, `subtotal`, `tax`, or `duty`. */ amountType?: 'total' | 'subtotal' | 'tax' | 'duty'; /** Any `ReactNode` elements. */ children?: React.ReactNode; } +type CartCostProps = Omit, 'data'> & + CartCostPropsBase; + /** - * The `CartCost` component renders a `Money` component with the - * cost associated with the `amountType` prop. If no `amountType` prop is specified, then it defaults to `totalAmount`. -Depends on `useCart()` and must be a child of `` + * The `CartCost` component renders a `Money` component with the cost associated with the `amountType` prop. + * If no `amountType` prop is specified, then it defaults to `totalAmount`. + * Depends on `useCart()` and must be a child of `` */ -export function CartCost( - props: Omit, 'data'> & CartCostProps -) { +export function CartCost(props: CartCostProps) { const {cost} = useCart(); const {amountType = 'total', children, ...passthroughProps} = props; let amount; diff --git a/packages/react/src/CartLinePrice.doc.ts b/packages/react/src/CartLinePrice.doc.ts new file mode 100644 index 0000000000..87b7e5bf1a --- /dev/null +++ b/packages/react/src/CartLinePrice.doc.ts @@ -0,0 +1,45 @@ +import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'CartLinePrice', + category: 'components', + isVisualComponent: false, + related: [ + { + name: 'Money', + type: 'component', + url: '/api/react-storefront-kit/components/money', + }, + ], + description: ` + The \`CartLinePrice\` component renders a \`Money\` component for the cart line merchandise's price or compare at price. + `, + type: 'component', + defaultExample: { + description: 'I am the default example', + codeblock: { + tabs: [ + { + title: 'JavaScript', + code: './CartLinePrice.example.jsx', + language: 'jsx', + }, + { + title: 'TypeScript', + code: './CartLinePrice.example.tsx', + language: 'tsx', + }, + ], + title: 'Example code', + }, + }, + definitions: [ + { + title: 'Props', + type: 'CartLinePriceProps', + description: '', + }, + ], +}; + +export default data; diff --git a/packages/react/src/CartLinePrice.example.jsx b/packages/react/src/CartLinePrice.example.jsx new file mode 100644 index 0000000000..da854a2376 --- /dev/null +++ b/packages/react/src/CartLinePrice.example.jsx @@ -0,0 +1,5 @@ +import {CartLinePrice} from '@shopify/storefront-kit-react'; + +export default function ProductCartLinePrice({cartLine}) { + return ; +} diff --git a/packages/react/src/CartLinePrice.example.tsx b/packages/react/src/CartLinePrice.example.tsx new file mode 100644 index 0000000000..4d26af536f --- /dev/null +++ b/packages/react/src/CartLinePrice.example.tsx @@ -0,0 +1,6 @@ +import {CartLinePrice} from '@shopify/storefront-kit-react'; +import type {CartLine} from '@shopify/storefront-kit-react/storefront-api-types'; + +export default function ProductCartLinePrice({cartLine}: {cartLine: CartLine}) { + return ; +} diff --git a/packages/react/src/CartLinePrice.tsx b/packages/react/src/CartLinePrice.tsx index 54f8d1a53b..536c576ec4 100644 --- a/packages/react/src/CartLinePrice.tsx +++ b/packages/react/src/CartLinePrice.tsx @@ -2,20 +2,20 @@ import {Money} from './Money.js'; import {CartLine} from './storefront-api-types.js'; import {PartialDeep} from 'type-fest'; -interface CartLinePriceProps { +interface CartLinePricePropsBase { /** A [CartLine object](https://shopify.dev/api/storefront/reference/objects/CartLine). */ data: PartialDeep; /** The type of price. Valid values:`regular` (default) or `compareAt`. */ priceType?: 'regular' | 'compareAt'; } +type CartLinePriceProps = Omit, 'data'> & + CartLinePricePropsBase; + /** - * The `CartLinePrice` component renders a `Money` component for the cart line merchandise's price or - * compare at price. + * The `CartLinePrice` component renders a `Money` component for the cart line merchandise's price or compare at price. */ -export function CartLinePrice( - props: Omit, 'data'> & CartLinePriceProps -) { +export function CartLinePrice(props: CartLinePriceProps) { const {data: cartLine, priceType = 'regular', ...passthroughProps} = props; if (cartLine == null) { diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 7483c2762e..6d0e5da60e 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,4 +1,21 @@ export {AddToCartButton} from './AddToCartButton.js'; +export { + AnalyticsEventName, + AnalyticsPageType, + ShopifyAppSource, +} from './analytics-constants.js'; +export type { + ClientBrowserParameters, + ShopifyPageViewPayload, + ShopifyPageView, + ShopifyAddToCartPayload, + ShopifyAddToCart, + ShopifyAnalyticsPayload, + ShopifyAnalytics, + ShopifyAnalyticsProduct, + ShopifyCookies, +} from './analytics-types.js'; +export {sendShopifyAnalytics, getClientBrowserParameters} from './analytics.js'; export {BuyNowButton} from './BuyNowButton.js'; export type { CartState, @@ -8,8 +25,11 @@ export type { CartAction, } from './cart-types.js'; export {CartCheckoutButton} from './CartCheckoutButton.js'; +export {CartCost} from './CartCost.js'; +export {CartLinePrice} from './CartLinePrice.js'; export {CartProvider, useCart} from './CartProvider.js'; export {storefrontApiCustomScalars} from './codegen.helpers.js'; +export {getShopifyCookies} from './cookies-utils.js'; export {ExternalVideo} from './ExternalVideo.js'; export {flattenConnection} from './flatten-connection.js'; export {Image} from './Image.js'; @@ -30,23 +50,5 @@ export type { } from './storefront-api-response.types.js'; export {createStorefrontClient} from './storefront-client.js'; export {useMoney} from './useMoney.js'; -export {Video} from './Video.js'; -export { - AnalyticsEventName, - AnalyticsPageType, - ShopifyAppSource, -} from './analytics-constants.js'; -export type { - ClientBrowserParameters, - ShopifyPageViewPayload, - ShopifyPageView, - ShopifyAddToCartPayload, - ShopifyAddToCart, - ShopifyAnalyticsPayload, - ShopifyAnalytics, - ShopifyAnalyticsProduct, - ShopifyCookies, -} from './analytics-types.js'; export {useShopifyCookies} from './useShopifyCookies.js'; -export {getShopifyCookies} from './cookies-utils.js'; -export {sendShopifyAnalytics, getClientBrowserParameters} from './analytics.js'; +export {Video} from './Video.js';