diff --git a/examples/algolia/src/components/product/ChildProduct.tsx b/examples/algolia/src/components/product/ChildProduct.tsx
deleted file mode 100644
index f29d4d94..00000000
--- a/examples/algolia/src/components/product/ChildProduct.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import type { IChildProduct } from "../../lib/types/product-types";
-import ProductVariations from "./ProductVariations";
-import ProductContainer from "./ProductContainer";
-
-interface IChildProductDetail {
- childProduct: IChildProduct;
-}
-
-const ChildProductDetail = ({
- childProduct,
-}: IChildProductDetail): JSX.Element => {
- const { product, baseProduct, variations, variationsMatrix } = childProduct;
- return (
-
- {variations && (
-
- )}
-
- );
-};
-
-export default ChildProductDetail;
diff --git a/examples/algolia/src/components/product/ProductComponents.tsx b/examples/algolia/src/components/product/ProductComponents.tsx
deleted file mode 100644
index 48dae906..00000000
--- a/examples/algolia/src/components/product/ProductComponents.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import { Fragment } from "react";
-import { ProductResponse } from "@moltin/sdk";
-
-interface IProductComponentsProps {
- components: ProductResponse[];
- product: ProductResponse;
-}
-
-const ProductComponents = ({
- components,
- product,
-}: IProductComponentsProps): JSX.Element => {
- return (
-
- {Object.keys(product.attributes.components).map((cmpName) => {
- const allOptions = product.attributes.components[cmpName].options;
- const bundle_configuration = product.meta.bundle_configuration;
- return (
-
-
{cmpName}
- {bundle_configuration ? (
-
-
- {allOptions.map(({ id, quantity }, index) => {
- const optionData = components.find(
- (item) => item.id === id,
- )!;
- return (
-
-
- {allOptions.length > 1 &&
- index + 1 !== allOptions.length ? (
-
- ) : null}
-
- );
- })}
-
-
- ) : null}
-
- );
- })}
-
- );
-};
-
-export default ProductComponents;
diff --git a/examples/algolia/src/components/product/ProductContainer.tsx b/examples/algolia/src/components/product/ProductContainer.tsx
index c07d9c3f..8d30c054 100644
--- a/examples/algolia/src/components/product/ProductContainer.tsx
+++ b/examples/algolia/src/components/product/ProductContainer.tsx
@@ -3,32 +3,35 @@ import ProductSummary from "./ProductSummary";
import ProductDetails from "./ProductDetails";
import ProductExtensions from "./ProductExtensions";
import CartActions from "./CartActions";
-import { IBase } from "../../lib/types/product-types";
import { ReactElement } from "react";
+import { ShopperProduct } from "@elasticpath/react-shopper-hooks";
+import { Form } from "formik";
interface IProductContainer {
- productBase: IBase;
+ product: ShopperProduct;
children?: ReactElement;
}
export default function ProductContainer({
- productBase: { product, main_image, otherImages },
+ product: { response, main_image, otherImages },
children,
}: IProductContainer): JSX.Element {
- const { extensions } = product.attributes;
+ const { extensions } = response.attributes;
return (
{main_image && (
)}
-
-
- {children}
-
- {extensions &&
}
-
-
+
);
diff --git a/examples/algolia/src/components/product/ProductDetails.tsx b/examples/algolia/src/components/product/ProductDetails.tsx
index 48a81aba..08b69d7a 100644
--- a/examples/algolia/src/components/product/ProductDetails.tsx
+++ b/examples/algolia/src/components/product/ProductDetails.tsx
@@ -1,11 +1,10 @@
-import type { ProductResponse } from "@moltin/sdk";
import { useContext } from "react";
import { ProductContext } from "../../lib/product-util";
import clsx from "clsx";
+import type { ShopperProduct } from "@elasticpath/react-shopper-hooks";
interface IProductDetails {
- product: ProductResponse;
- isModal?: boolean;
+ product: ShopperProduct["response"];
}
const ProductDetails = ({ product }: IProductDetails): JSX.Element => {
diff --git a/examples/algolia/src/components/product/ProductSummary.tsx b/examples/algolia/src/components/product/ProductSummary.tsx
index 48bb034c..2c00b22a 100644
--- a/examples/algolia/src/components/product/ProductSummary.tsx
+++ b/examples/algolia/src/components/product/ProductSummary.tsx
@@ -1,12 +1,12 @@
-import type { ProductResponse } from "@moltin/sdk";
import { useContext } from "react";
import { ProductContext } from "../../lib/product-util";
import Price from "./Price";
import StrikePrice from "./StrikePrice";
import clsx from "clsx";
+import type { ShopperProduct } from "@elasticpath/react-shopper-hooks";
interface IProductSummary {
- product: ProductResponse;
+ product: ShopperProduct["response"];
}
const ProductSummary = ({ product }: IProductSummary): JSX.Element => {
diff --git a/examples/algolia/src/components/product/SimpleProduct.tsx b/examples/algolia/src/components/product/SimpleProduct.tsx
index 8c80e8db..793740c3 100644
--- a/examples/algolia/src/components/product/SimpleProduct.tsx
+++ b/examples/algolia/src/components/product/SimpleProduct.tsx
@@ -1,22 +1,45 @@
-import { ISimpleProduct } from "../../lib/types/product-types";
-import ProductComponents from "./ProductComponents";
+import type { SimpleProduct } from "@elasticpath/react-shopper-hooks";
import ProductContainer from "./ProductContainer";
+import { useCallback } from "react";
+import {
+ SimpleProductProvider,
+ useCart,
+ useSimpleProduct,
+} from "@elasticpath/react-shopper-hooks";
+import { Formik } from "formik";
interface ISimpleProductDetail {
- simpleProduct: ISimpleProduct;
+ simpleProduct: SimpleProduct;
}
-const SimpleProductDetail = ({
+function SimpleProductDetail({
simpleProduct,
-}: ISimpleProductDetail): JSX.Element => {
- const { product, component_products } = simpleProduct;
+}: ISimpleProductDetail): JSX.Element {
return (
-
- {component_products && (
-
- )}
-
+
+
+
);
-};
+}
+
+function SimpleProductContainer(): JSX.Element {
+ const { addProductToCart } = useCart();
+ const { product } = useSimpleProduct();
+
+ const submit = useCallback(async () => {
+ await addProductToCart(product.response.id, 1);
+ }, [product, addProductToCart]);
+
+ return (
+
{
+ await submit();
+ }}
+ >
+
+
+ );
+}
export default SimpleProductDetail;
diff --git a/examples/algolia/src/components/product/bundles/BundleProduct.tsx b/examples/algolia/src/components/product/bundles/BundleProduct.tsx
new file mode 100644
index 00000000..1c560d81
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/BundleProduct.tsx
@@ -0,0 +1,68 @@
+import ProductComponents from "./ProductComponents";
+import ProductContainer from "../ProductContainer";
+import { Formik } from "formik";
+import {
+ BundleProduct,
+ BundleProductProvider,
+ useBundle,
+ useCart,
+} from "@elasticpath/react-shopper-hooks";
+import { useCallback, useMemo } from "react";
+import {
+ FormSelectedOptions,
+ formSelectedOptionsToData,
+ selectedOptionsToFormValues,
+} from "./form-parsers";
+import { createBundleFormSchema } from "./validation-schema";
+import { toFormikValidate } from "zod-formik-adapter";
+
+interface IBundleProductDetail {
+ bundleProduct: BundleProduct;
+}
+
+const BundleProductDetail = ({
+ bundleProduct,
+}: IBundleProductDetail): JSX.Element => {
+ return (
+
+
+
+ );
+};
+
+function BundleProductContainer(): JSX.Element {
+ const { configuredProduct, selectedOptions, components } = useBundle();
+ const { addBundleProductToCart } = useCart();
+
+ const submit = useCallback(
+ async (values: { selectedOptions: FormSelectedOptions }) => {
+ await addBundleProductToCart(
+ configuredProduct.response.id,
+ formSelectedOptionsToData(values.selectedOptions),
+ 1,
+ );
+ },
+ [addBundleProductToCart, configuredProduct.response.id],
+ );
+
+ const validationSchema = useMemo(
+ () => createBundleFormSchema(components),
+ [components],
+ );
+
+ return (
+
submit(values)}
+ >
+
+
+
+
+ );
+}
+
+export default BundleProductDetail;
diff --git a/examples/algolia/src/components/product/bundles/ProductComponent.tsx b/examples/algolia/src/components/product/bundles/ProductComponent.tsx
new file mode 100644
index 00000000..9ad098ae
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/ProductComponent.tsx
@@ -0,0 +1,156 @@
+import {
+ useBundleComponent,
+ useBundle,
+ useBundleComponentOption,
+} from "@elasticpath/react-shopper-hooks";
+import type { BundleComponent } from "@elasticpath/react-shopper-hooks";
+import { ProductComponentOption, ProductResponse } from "@moltin/sdk";
+import { sortByOrder } from "./sort-by-order";
+import { useField, useFormikContext } from "formik";
+
+import clsx from "clsx";
+import Image from "next/image";
+import * as React from "react";
+
+export const ProductComponent = ({
+ component,
+ componentLookupKey,
+}: {
+ component: BundleComponent;
+ componentLookupKey: string;
+}): JSX.Element => {
+ const { componentProducts } = useBundle();
+
+ const { name } = component;
+
+ const { errors, touched } = useFormikContext<{
+ selectedOptions: any;
+ }>();
+
+ return (
+
+ );
+};
+
+function CheckboxComponentOptions({
+ options,
+ componentLookupKey,
+}: {
+ componentProducts: ProductResponse[];
+ options: ProductComponentOption[];
+ max?: number;
+ min?: number;
+ componentLookupKey: string;
+}): JSX.Element {
+ return (
+
+ {options.sort(sortByOrder).map((option) => {
+ return (
+
+ );
+ })}
+
+ );
+}
+
+function CheckboxComponentOption({
+ option,
+ componentKey,
+}: {
+ option: ProductComponentOption;
+ componentKey: string;
+}): JSX.Element {
+ const { selected, component } = useBundleComponent(componentKey);
+ const { optionProduct, mainImage } = useBundleComponentOption(
+ componentKey,
+ option.id,
+ );
+
+ const selectedOptionKey = Object.keys(selected);
+
+ const reachedMax =
+ !!component.max && Object.keys(selected).length === component.max;
+
+ const isDisabled =
+ reachedMax &&
+ !selectedOptionKey.some((optionKey) => optionKey === option.id);
+
+ const name = `selectedOptions.${componentKey}`;
+ const inputId = `${name}.${option.id}`;
+
+ const [field] = useField({
+ name,
+ type: "checkbox",
+ value: JSON.stringify({ [option.id]: option.quantity }),
+ disabled: isDisabled,
+ id: inputId,
+ });
+
+ return (
+
+
+
{optionProduct.attributes.name}
+
+ {optionProduct.meta.display_price?.without_tax.formatted}
+
+
+ );
+}
diff --git a/examples/algolia/src/components/product/bundles/ProductComponents.tsx b/examples/algolia/src/components/product/bundles/ProductComponents.tsx
new file mode 100644
index 00000000..d00ae8a2
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/ProductComponents.tsx
@@ -0,0 +1,33 @@
+import { useBundle } from "@elasticpath/react-shopper-hooks";
+import { ProductComponent } from "./ProductComponent";
+import { useFormikContext } from "formik";
+import { useEffect } from "react";
+import { FormSelectedOptions, formSelectedOptionsToData } from "./form-parsers";
+
+const ProductComponents = (): JSX.Element => {
+ const { components, updateSelectedOptions } = useBundle();
+
+ const { values } = useFormikContext<{
+ selectedOptions: FormSelectedOptions;
+ }>();
+
+ useEffect(() => {
+ updateSelectedOptions(formSelectedOptionsToData(values.selectedOptions));
+ }, [values, updateSelectedOptions]);
+
+ return (
+
+ {Object.keys(components).map((key) => {
+ return (
+
+ );
+ })}
+
+ );
+};
+
+export default ProductComponents;
diff --git a/examples/algolia/src/components/product/bundles/form-parsers.test.ts b/examples/algolia/src/components/product/bundles/form-parsers.test.ts
new file mode 100644
index 00000000..591fca06
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/form-parsers.test.ts
@@ -0,0 +1,72 @@
+import { describe, expect, test } from "vitest";
+import { BundleConfigurationSelectedOptions } from "@elasticpath/react-shopper-hooks";
+import {
+ formSelectedOptionsToData,
+ selectedOptionsToFormValues,
+} from "./form-parsers";
+
+describe("form-parsers", () => {
+ test("component options to form", () => {
+ const data: BundleConfigurationSelectedOptions = {
+ plants: {
+ "a158ffa0-5d16-4325-8dcc-be8acd55eecf": 1,
+ "2131231dwadwd12-1d21d2dqw-dd12dqwdaw": 1,
+ },
+ pots: {
+ "fc520b37-a709-4032-99b3-8d4ecc990027": 1,
+ },
+ tools: {},
+ };
+
+ const expectedResult = {
+ plants: [
+ JSON.stringify({
+ "a158ffa0-5d16-4325-8dcc-be8acd55eecf": 1,
+ }),
+ JSON.stringify({
+ "2131231dwadwd12-1d21d2dqw-dd12dqwdaw": 1,
+ }),
+ ],
+ pots: [
+ JSON.stringify({
+ "fc520b37-a709-4032-99b3-8d4ecc990027": 1,
+ }),
+ ],
+ tools: [],
+ };
+
+ expect(selectedOptionsToFormValues(data)).toEqual(expectedResult);
+ });
+
+ test("form to component options", () => {
+ const data = {
+ plants: [
+ JSON.stringify({
+ "a158ffa0-5d16-4325-8dcc-be8acd55eecf": 1,
+ }),
+ JSON.stringify({
+ "2131231dwadwd12-1d21d2dqw-dd12dqwdaw": 1,
+ }),
+ ],
+ pots: [
+ JSON.stringify({
+ "fc520b37-a709-4032-99b3-8d4ecc990027": 1,
+ }),
+ ],
+ tools: [],
+ };
+
+ const expectedResult: BundleConfigurationSelectedOptions = {
+ plants: {
+ "a158ffa0-5d16-4325-8dcc-be8acd55eecf": 1,
+ "2131231dwadwd12-1d21d2dqw-dd12dqwdaw": 1,
+ },
+ pots: {
+ "fc520b37-a709-4032-99b3-8d4ecc990027": 1,
+ },
+ tools: {},
+ };
+
+ expect(formSelectedOptionsToData(data)).toEqual(expectedResult);
+ });
+});
diff --git a/examples/algolia/src/components/product/bundles/form-parsers.ts b/examples/algolia/src/components/product/bundles/form-parsers.ts
new file mode 100644
index 00000000..6966c7b4
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/form-parsers.ts
@@ -0,0 +1,51 @@
+import { BundleConfigurationSelectedOptions } from "@elasticpath/react-shopper-hooks";
+
+export interface FormSelectedOptions {
+ [key: string]: string[];
+}
+
+export function selectedOptionsToFormValues(
+ selectedOptions: BundleConfigurationSelectedOptions,
+): FormSelectedOptions {
+ return Object.keys(selectedOptions).reduce((acc, componentKey) => {
+ const componentOptions = selectedOptions[componentKey];
+
+ return {
+ ...acc,
+ [componentKey]: Object.keys(componentOptions).reduce(
+ (innerAcc, optionKey) => {
+ return [
+ ...innerAcc,
+ JSON.stringify({ [optionKey]: componentOptions[optionKey] }),
+ ];
+ },
+ [] as string[],
+ ),
+ };
+ }, {});
+}
+
+export function formSelectedOptionsToData(
+ selectedOptions: FormSelectedOptions,
+): BundleConfigurationSelectedOptions {
+ return Object.keys(selectedOptions).reduce((acc, componentKey) => {
+ const componentOptions = selectedOptions[componentKey];
+
+ return {
+ ...acc,
+ [componentKey]: componentOptions.reduce(
+ (innerAcc, optionStr) => {
+ const parsed = JSON.parse(
+ optionStr,
+ ) as BundleConfigurationSelectedOptions[0];
+
+ return {
+ ...innerAcc,
+ ...parsed,
+ };
+ },
+ {} as BundleConfigurationSelectedOptions[0],
+ ),
+ };
+ }, {});
+}
diff --git a/examples/algolia/src/components/product/bundles/sort-by-order.ts b/examples/algolia/src/components/product/bundles/sort-by-order.ts
new file mode 100644
index 00000000..672821f8
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/sort-by-order.ts
@@ -0,0 +1,6 @@
+export function sortByOrder(
+ { sort_order: a }: { sort_order?: number | null },
+ { sort_order: b }: { sort_order?: number | null },
+): number {
+ return a || b ? (!a ? -1 : !b ? 1 : a - b) : 0;
+}
diff --git a/examples/algolia/src/components/product/bundles/validation-schema.test.ts b/examples/algolia/src/components/product/bundles/validation-schema.test.ts
new file mode 100644
index 00000000..bfdfb103
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/validation-schema.test.ts
@@ -0,0 +1,136 @@
+import { describe, expect, test } from "vitest";
+import { BundleComponents } from "@elasticpath/react-shopper-hooks";
+
+import { createBundleFormSchema } from "./validation-schema";
+import { DeepPartial } from "../../../lib/types/deep-partial";
+
+describe("validation-schema", () => {
+ test("createBundleFormSchema valid", () => {
+ const bundleComponents: DeepPartial
= {
+ plants: {
+ max: 1,
+ min: 1,
+ name: "Plants",
+ options: [
+ {
+ id: "a158ffa0-5d16-4325-8dcc-be8acd55eecf",
+ quantity: 1,
+ type: "product",
+ },
+ ],
+ sort_order: 1,
+ },
+ pots: {
+ max: 1,
+ min: 1,
+ name: "Pots",
+ options: [
+ {
+ id: "fc520b37-a709-4032-99b3-8d4ecc990027",
+ quantity: 1,
+ type: "product",
+ },
+ {
+ id: "28c13338-07f8-4e40-85b0-e85c0917fb28",
+ quantity: 1,
+ type: "product",
+ },
+ ],
+ sort_order: 2,
+ },
+ tools: {
+ max: 1,
+ min: 0,
+ name: "Tools",
+ options: [
+ {
+ id: "7ffe107d-c5bd-4de4-b8f0-a58d90cb3cd3",
+ quantity: 1,
+ type: "product",
+ },
+ ],
+ sort_order: 3,
+ },
+ };
+
+ const validData = {
+ selectedOptions: {
+ plants: ['{"a158ffa0-5d16-4325-8dcc-be8acd55eecf":1}'],
+ pots: ['{"fc520b37-a709-4032-99b3-8d4ecc990027":1}'],
+ tools: [],
+ },
+ };
+
+ const result = createBundleFormSchema(
+ bundleComponents as BundleComponents,
+ ).safeParse(validData);
+
+ expect(result.success).toEqual(true);
+ });
+
+ test("createBundleFormSchema with invalid min max values", () => {
+ const bundleComponents: DeepPartial = {
+ plants: {
+ max: 1,
+ min: 1,
+ name: "Plants",
+ options: [
+ {
+ id: "a158ffa0-5d16-4325-8dcc-be8acd55eecf",
+ quantity: 1,
+ type: "product",
+ },
+ ],
+ sort_order: 1,
+ },
+ pots: {
+ max: 1,
+ min: 1,
+ name: "Pots",
+ options: [
+ {
+ id: "fc520b37-a709-4032-99b3-8d4ecc990027",
+ quantity: 1,
+ type: "product",
+ },
+ {
+ id: "28c13338-07f8-4e40-85b0-e85c0917fb28",
+ quantity: 1,
+ type: "product",
+ },
+ ],
+ sort_order: 2,
+ },
+ tools: {
+ max: 1,
+ min: 0,
+ name: "Tools",
+ options: [
+ {
+ id: "7ffe107d-c5bd-4de4-b8f0-a58d90cb3cd3",
+ quantity: 1,
+ type: "product",
+ },
+ ],
+ sort_order: 3,
+ },
+ };
+
+ const validData = {
+ selectedOptions: {
+ plants: [
+ '{"a158ffa0-5d16-4325-8dcc-be8acd55eecf":1}',
+ '{"awdawdawdaw-awdawjnawd-awdauunwda":1}',
+ ],
+ pots: [],
+ tools: [],
+ },
+ };
+
+ const result = createBundleFormSchema(
+ bundleComponents as BundleComponents,
+ ).safeParse(validData);
+
+ expect(result.success).toEqual(false);
+ });
+});
diff --git a/examples/algolia/src/components/product/bundles/validation-schema.ts b/examples/algolia/src/components/product/bundles/validation-schema.ts
new file mode 100644
index 00000000..4e0792ac
--- /dev/null
+++ b/examples/algolia/src/components/product/bundles/validation-schema.ts
@@ -0,0 +1,38 @@
+import { z } from "zod";
+import type {
+ BundleComponent,
+ BundleComponents,
+} from "@elasticpath/react-shopper-hooks";
+
+export const createBundleComponentSchema = (component: BundleComponent) => {
+ let schema = z.array(z.string());
+
+ const { min, max } = component;
+
+ if (min) {
+ schema = schema.min(min, `Must select at least ${min} options`);
+ }
+
+ if (max) {
+ schema = schema.max(max, `Must select no more than ${max} options`);
+ }
+ return schema;
+};
+
+export const createBundleFormSchema = (bundleComponents: BundleComponents) => {
+ const selectedOptionsSchema = Object.keys(bundleComponents).reduce(
+ (acc, componentKey) => {
+ return {
+ ...acc,
+ [componentKey]: createBundleComponentSchema(
+ bundleComponents[componentKey],
+ ),
+ };
+ },
+ {} as Record>,
+ );
+
+ return z.object({
+ selectedOptions: z.object(selectedOptionsSchema),
+ });
+};
diff --git a/examples/algolia/src/components/product/carousel/ProductCarousel.module.css b/examples/algolia/src/components/product/carousel/ProductCarousel.module.css
index 0d3611c6..31664240 100644
--- a/examples/algolia/src/components/product/carousel/ProductCarousel.module.css
+++ b/examples/algolia/src/components/product/carousel/ProductCarousel.module.css
@@ -10,7 +10,7 @@
.product-carousel-selected {
border-radius: 0.375rem;
- border: 2px solid #0033cc;
+ border: 2px solid #2bcc7e;
padding: 0.125rem;
}
diff --git a/examples/algolia/src/components/product/variations/ProductVariationColor.tsx b/examples/algolia/src/components/product/variations/ProductVariationColor.tsx
index 022305bc..d60cbd37 100644
--- a/examples/algolia/src/components/product/variations/ProductVariationColor.tsx
+++ b/examples/algolia/src/components/product/variations/ProductVariationColor.tsx
@@ -1,5 +1,6 @@
import clsx from "clsx";
import { colorLookup } from "../../../lib/color-lookup";
+import type { useVariationProduct } from "@elasticpath/react-shopper-hooks";
interface ProductVariationOption {
id: string;
@@ -7,17 +8,15 @@ interface ProductVariationOption {
name: string;
}
-export type UpdateOptionHandler = (
- variationId: string,
-) => (optionId: string) => void;
-
interface IProductVariation {
variation: {
id: string;
name: string;
options: ProductVariationOption[];
};
- updateOptionHandler: UpdateOptionHandler;
+ updateOptionHandler: ReturnType<
+ typeof useVariationProduct
+ >["updateSelectedOptions"];
selectedOptionId?: string;
}
@@ -39,11 +38,12 @@ const ProductVariationColor = ({
key={o.id}
>
))}
diff --git a/examples/algolia/src/components/product/variations/ProductVariationStandard.tsx b/examples/algolia/src/components/product/variations/ProductVariationStandard.tsx
index a9315791..a326191c 100644
--- a/examples/algolia/src/components/product/variations/ProductVariationStandard.tsx
+++ b/examples/algolia/src/components/product/variations/ProductVariationStandard.tsx
@@ -1,4 +1,5 @@
import clsx from "clsx";
+import type { useVariationProduct } from "@elasticpath/react-shopper-hooks";
interface ProductVariationOption {
id: string;
@@ -16,7 +17,9 @@ interface IProductVariation {
name: string;
options: ProductVariationOption[];
};
- updateOptionHandler: UpdateOptionHandler;
+ updateOptionHandler: ReturnType<
+ typeof useVariationProduct
+ >["updateSelectedOptions"];
selectedOptionId?: string;
}
@@ -31,6 +34,7 @@ const ProductVariationStandard = ({