From a7501f64df4576ff89fcb2900a2477cbc0894335 Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Thu, 20 Feb 2025 23:41:11 -0800 Subject: [PATCH 01/13] add url overrides --- .../useProductInfo/useProductInfo.test.js | 36 +++++++++++++++++-- src/hooks/useProduct.ts | 11 +++--- src/stories/components/CioPlp/CioPlpProps.md | 9 +++-- src/types.ts | 4 +++ src/utils/itemFieldGetters.ts | 29 +++++++++++++-- 5 files changed, 77 insertions(+), 12 deletions(-) diff --git a/spec/hooks/useProductInfo/useProductInfo.test.js b/spec/hooks/useProductInfo/useProductInfo.test.js index bcd2556d..723bf0cc 100644 --- a/spec/hooks/useProductInfo/useProductInfo.test.js +++ b/spec/hooks/useProductInfo/useProductInfo.test.js @@ -45,6 +45,9 @@ describe('Testing Hook: useProductInfo', () => { getPrice: () => {}, getSwatches: () => {}, getSwatchPreview: () => {}, + getName: () => {}, + getUrl: () => {}, + getImageUrl: () => {}, }, }, }); @@ -55,13 +58,40 @@ describe('Testing Hook: useProductInfo', () => { } = result; expect(productSwatch).not.toBeNull(); - expect(itemName).toEqual(transformedItem.itemName); - expect(itemImageUrl).toEqual(transformedItem.imageUrl); - expect(itemUrl).toEqual(transformedItem.url); + expect(itemName).toBeUndefined(); + expect(itemImageUrl).toBeUndefined(); + expect(itemUrl).toBeUndefined(); expect(itemPrice).toBeUndefined(); }); }); + it('Should return properly with getters that override defaults', async () => { + const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }), { + initialProps: { + itemFieldGetters: { + getPrice: () => 'override', + getSwatches: () => [], + getSwatchPreview: () => 'override', + getName: () => 'override', + getUrl: () => 'override', + getImageUrl: () => 'override', + }, + }, + }); + + await waitFor(() => { + const { + current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, + } = result; + + expect(itemName).toEqual('override'); + expect(itemUrl).toEqual('override'); + expect(itemImageUrl).toEqual('override'); + expect(itemPrice).toEqual('override'); + expect(productSwatch).not.toBeNull(); + }); + }); + it('Should return correctly after different variation is selected', async () => { const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem })); diff --git a/src/hooks/useProduct.ts b/src/hooks/useProduct.ts index d6d044fa..5d152fcd 100644 --- a/src/hooks/useProduct.ts +++ b/src/hooks/useProduct.ts @@ -12,11 +12,14 @@ const useProductInfo: UseProductInfo = ({ item }) => { } const getPrice = tryCatchify(state?.itemFieldGetters?.getPrice); + const getImageUrl = tryCatchify(state?.itemFieldGetters?.getImageUrl); + const getUrl = tryCatchify(state?.itemFieldGetters?.getUrl); + const getName = tryCatchify(state?.itemFieldGetters?.getName); - const itemName = productSwatch?.selectedVariation?.itemName || item.itemName; - const itemPrice = productSwatch?.selectedVariation?.price || getPrice(item); - const itemImageUrl = productSwatch?.selectedVariation?.imageUrl || item.imageUrl; - const itemUrl = productSwatch?.selectedVariation?.url || item.url; + const itemName = getName(item, productSwatch?.selectedVariation); + const itemPrice = getPrice(item, productSwatch?.selectedVariation); + const itemImageUrl = getImageUrl(item, productSwatch?.selectedVariation); + const itemUrl = getUrl(item, productSwatch?.selectedVariation); return { productSwatch, diff --git a/src/stories/components/CioPlp/CioPlpProps.md b/src/stories/components/CioPlp/CioPlpProps.md index 0a77c1fa..4af96bee 100644 --- a/src/stories/components/CioPlp/CioPlpProps.md +++ b/src/stories/components/CioPlp/CioPlpProps.md @@ -10,9 +10,12 @@ Formatters will be used to modify how certain fields are rendered ItemFieldGetters maps the fields sent in the catalog feeds to the fields the libary expects for rendering -| property | type | description | -| -------- | ------------------------ | ------------------ | -| getPrice | `(item: Item) => number` | Get price funciton | +| property | type | description | +| ----------- | ------------------------ | ---------------------- | +| getPrice | `(item: Item) => number` | Get price funciton | +| getImageUrl | `(item: Item) => string` | Get image url funciton | +| getUrl | `(item: Item) => stirng` | Get href url funciton | +| getName | `(item: Item) => string` | Get item name funciton | ### `Callbacks` diff --git a/src/types.ts b/src/types.ts index a4eba4b6..9690883e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,6 +41,9 @@ export type CioClientOptions = Omit number; + getUrl: (item: Item | Variation) => string; + getImageUrl: (item: Item | Variation) => string; + getName: (item: Item | Variation) => string; getSwatchPreview: (variation: Variation) => string; getSwatches: ( item: Item, @@ -204,6 +207,7 @@ export interface SwatchItem { price?: number; swatchPreview: string; variationId?: string; + variation?: Variation; } export interface PlpBrowseData { diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index d310229e..15383b9d 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -1,8 +1,32 @@ import { ItemFieldGetters, Item, SwatchItem, Variation } from '../types'; // eslint-disable-next-line import/prefer-default-export -export function getPrice(item: Item | Variation): number { - return item.data.price; +export function getPrice(item: Item | Variation, selectedSwatch?: SwatchItem | undefined): number { + return selectedSwatch?.price || selectedSwatch?.variation?.data?.price || item?.data?.price; +} + +// eslint-disable-next-line import/prefer-default-export +export function getImageUrl( + item: Item | Variation, + selectedSwatch?: SwatchItem | undefined, +): string | undefined { + return selectedSwatch?.imageUrl || selectedSwatch?.variation?.imageUrl || item?.imageUrl; +} + +// eslint-disable-next-line import/prefer-default-export +export function getUrl( + item: Item | Variation, + selectedSwatch?: SwatchItem | undefined, +): string | undefined { + return selectedSwatch?.url || selectedSwatch?.variation?.url || item.url; +} + +// eslint-disable-next-line import/prefer-default-export +export function getName( + item: Item | Variation, + selectedSwatch?: SwatchItem | undefined, +): string | undefined { + return selectedSwatch?.itemName || selectedSwatch?.variation?.itemName || item.itemName; } export function getSwatches( @@ -21,6 +45,7 @@ export function getSwatches( variationId: variation?.variationId, price: retrievePrice(variation), swatchPreview: retrieveSwatchPreview(variation), + variation, }); } }); From 7644ca4433e3729ebf44530a5e60d9847460542a Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Fri, 4 Apr 2025 09:25:12 -0700 Subject: [PATCH 02/13] add custom configs --- .../useProductInfo/useProductInfo.test.js | 20 +++++++++++++++++++ src/hooks/useCioPlpProvider.ts | 3 +++ src/types.ts | 6 ++++++ src/utils/itemFieldGetters.ts | 7 +++++++ 4 files changed, 36 insertions(+) diff --git a/spec/hooks/useProductInfo/useProductInfo.test.js b/spec/hooks/useProductInfo/useProductInfo.test.js index 723bf0cc..1712199f 100644 --- a/spec/hooks/useProductInfo/useProductInfo.test.js +++ b/spec/hooks/useProductInfo/useProductInfo.test.js @@ -92,6 +92,26 @@ describe('Testing Hook: useProductInfo', () => { }); }); + it.only('Should return image properly with overridden baseUrl', async () => { + const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }), { + initialProps: { + customConfigs: { imageBaseUrl: 'test.com ' }, + }, + }); + + await waitFor(() => { + const { + current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, + } = result; + + expect(productSwatch).not.toBeNull(); + expect(itemName).toEqual(transformedItem.itemName); + expect(itemImageUrl).toEqual(`test.com${transformedItem.imageUrl}`); + expect(itemUrl).toEqual(transformedItem.url); + expect(itemPrice).toEqual(transformedItem.data.price); + }); + }); + it('Should return correctly after different variation is selected', async () => { const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem })); diff --git a/src/hooks/useCioPlpProvider.ts b/src/hooks/useCioPlpProvider.ts index a4de5d6b..2ec09486 100644 --- a/src/hooks/useCioPlpProvider.ts +++ b/src/hooks/useCioPlpProvider.ts @@ -15,6 +15,7 @@ export default function useCioPlpProvider( itemFieldGetters, urlHelpers, staticRequestConfigs = {}, + customConfigs = {}, cioClient: customCioClient, } = props; @@ -27,6 +28,7 @@ export default function useCioPlpProvider( cioClientOptions, setCioClientOptions, staticRequestConfigs, + customConfigs, itemFieldGetters: { ...defaultGetters, ...itemFieldGetters }, formatters: { ...defaultFormatters, ...formatters }, callbacks: { ...callbacks }, @@ -40,6 +42,7 @@ export default function useCioPlpProvider( callbacks, urlHelpers, staticRequestConfigs, + customConfigs, ], ); diff --git a/src/types.ts b/src/types.ts index 9690883e..77c5e286 100644 --- a/src/types.ts +++ b/src/types.ts @@ -113,6 +113,10 @@ export interface UrlHelpers { defaultQueryStringMap: Readonly; } +export interface CustomConfigs { + imageBaseUrl?: string; +} + export interface RequestConfigs { // Search query?: string; @@ -147,6 +151,7 @@ export interface PlpContextValue { cioClientOptions: CioClientOptions; setCioClientOptions: React.Dispatch; staticRequestConfigs: RequestConfigs; + customConfigs: CustomConfigs; itemFieldGetters: ItemFieldGetters; formatters: Formatters; callbacks: Callbacks; @@ -229,6 +234,7 @@ export interface CioPlpProviderProps { initialSearchResponse?: SearchResponse; initialBrowseResponse?: GetBrowseResultsResponse; staticRequestConfigs?: Partial; + customConfigs?: Partial; } export type UseSortReturn = { diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index 15383b9d..88a5f3e4 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -9,7 +9,14 @@ export function getPrice(item: Item | Variation, selectedSwatch?: SwatchItem | u export function getImageUrl( item: Item | Variation, selectedSwatch?: SwatchItem | undefined, + options?: any, ): string | undefined { + const { imageBaseUrl } = options; + + console.log(options); + if (imageBaseUrl) { + return `${imageBaseUrl}${selectedSwatch?.imageUrl || selectedSwatch?.variation?.imageUrl || item?.imageUrl}`; + } return selectedSwatch?.imageUrl || selectedSwatch?.variation?.imageUrl || item?.imageUrl; } From ac27cf624636732f7571440ea964f77afc8c9b3a Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Fri, 11 Apr 2025 09:44:09 -0700 Subject: [PATCH 03/13] fix tests --- spec/components/CioPlp/CioPlp.server.test.jsx | 2 +- spec/hooks/useProductInfo/useProductInfo.test.js | 4 ++-- src/hooks/useProduct.ts | 4 +++- src/utils/itemFieldGetters.ts | 1 - 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/components/CioPlp/CioPlp.server.test.jsx b/spec/components/CioPlp/CioPlp.server.test.jsx index e46f410c..c46da3bf 100644 --- a/spec/components/CioPlp/CioPlp.server.test.jsx +++ b/spec/components/CioPlp/CioPlp.server.test.jsx @@ -57,7 +57,7 @@ describe('CioPlp React Server-Side Rendering', () => { , ); expect(html).toContain( - '
{"cioClient":null,"cioClientOptions":{},"staticRequestConfigs":{},"itemFieldGetters":{},"formatters":{},"callbacks":{},"urlHelpers":{"defaultQueryStringMap":{"query":"q","page":"page","offset":"offset","resultsPerPage":"numResults","filters":"filters","sortBy":"sortBy","sortOrder":"sortOrder","section":"section"}}}
', + '
{"cioClient":null,"cioClientOptions":{},"staticRequestConfigs":{},"customConfigs":{},"itemFieldGetters":{},"formatters":{},"callbacks":{},"urlHelpers":{"defaultQueryStringMap":{"query":"q","page":"page","offset":"offset","resultsPerPage":"numResults","filters":"filters","sortBy":"sortBy","sortOrder":"sortOrder","section":"section"}}}
', ); }); }); diff --git a/spec/hooks/useProductInfo/useProductInfo.test.js b/spec/hooks/useProductInfo/useProductInfo.test.js index 1712199f..bb916eac 100644 --- a/spec/hooks/useProductInfo/useProductInfo.test.js +++ b/spec/hooks/useProductInfo/useProductInfo.test.js @@ -92,10 +92,10 @@ describe('Testing Hook: useProductInfo', () => { }); }); - it.only('Should return image properly with overridden baseUrl', async () => { + it('Should return image properly with overridden baseUrl', async () => { const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }), { initialProps: { - customConfigs: { imageBaseUrl: 'test.com ' }, + customConfigs: { imageBaseUrl: 'test.com' }, }, }); diff --git a/src/hooks/useProduct.ts b/src/hooks/useProduct.ts index 5d152fcd..fa6cc7f2 100644 --- a/src/hooks/useProduct.ts +++ b/src/hooks/useProduct.ts @@ -18,7 +18,9 @@ const useProductInfo: UseProductInfo = ({ item }) => { const itemName = getName(item, productSwatch?.selectedVariation); const itemPrice = getPrice(item, productSwatch?.selectedVariation); - const itemImageUrl = getImageUrl(item, productSwatch?.selectedVariation); + const itemImageUrl = getImageUrl(item, productSwatch?.selectedVariation, { + imageBaseUrl: state.customConfigs.imageBaseUrl, + }); const itemUrl = getUrl(item, productSwatch?.selectedVariation); return { diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index 88a5f3e4..fe27ab72 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -13,7 +13,6 @@ export function getImageUrl( ): string | undefined { const { imageBaseUrl } = options; - console.log(options); if (imageBaseUrl) { return `${imageBaseUrl}${selectedSwatch?.imageUrl || selectedSwatch?.variation?.imageUrl || item?.imageUrl}`; } From 2183c4b51e920808cb315dbda32f54fd033e6de0 Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Thu, 17 Apr 2025 09:41:25 -0700 Subject: [PATCH 04/13] fix errors and types --- spec/hooks/useProductInfo/useProductInfo.test.js | 4 ++-- src/hooks/useProduct.ts | 4 ++-- src/stories/components/CioPlp/CioPlpProps.md | 2 +- src/types.ts | 8 ++++---- src/utils/itemFieldGetters.ts | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/hooks/useProductInfo/useProductInfo.test.js b/spec/hooks/useProductInfo/useProductInfo.test.js index bb916eac..ff4e809a 100644 --- a/spec/hooks/useProductInfo/useProductInfo.test.js +++ b/spec/hooks/useProductInfo/useProductInfo.test.js @@ -46,7 +46,7 @@ describe('Testing Hook: useProductInfo', () => { getSwatches: () => {}, getSwatchPreview: () => {}, getName: () => {}, - getUrl: () => {}, + getItemUrl: () => {}, getImageUrl: () => {}, }, }, @@ -73,7 +73,7 @@ describe('Testing Hook: useProductInfo', () => { getSwatches: () => [], getSwatchPreview: () => 'override', getName: () => 'override', - getUrl: () => 'override', + getItemUrl: () => 'override', getImageUrl: () => 'override', }, }, diff --git a/src/hooks/useProduct.ts b/src/hooks/useProduct.ts index fa6cc7f2..421183f1 100644 --- a/src/hooks/useProduct.ts +++ b/src/hooks/useProduct.ts @@ -13,7 +13,7 @@ const useProductInfo: UseProductInfo = ({ item }) => { const getPrice = tryCatchify(state?.itemFieldGetters?.getPrice); const getImageUrl = tryCatchify(state?.itemFieldGetters?.getImageUrl); - const getUrl = tryCatchify(state?.itemFieldGetters?.getUrl); + const getItemUrl = tryCatchify(state?.itemFieldGetters?.getItemUrl); const getName = tryCatchify(state?.itemFieldGetters?.getName); const itemName = getName(item, productSwatch?.selectedVariation); @@ -21,7 +21,7 @@ const useProductInfo: UseProductInfo = ({ item }) => { const itemImageUrl = getImageUrl(item, productSwatch?.selectedVariation, { imageBaseUrl: state.customConfigs.imageBaseUrl, }); - const itemUrl = getUrl(item, productSwatch?.selectedVariation); + const itemUrl = getItemUrl(item, productSwatch?.selectedVariation); return { productSwatch, diff --git a/src/stories/components/CioPlp/CioPlpProps.md b/src/stories/components/CioPlp/CioPlpProps.md index 4af96bee..f07caef0 100644 --- a/src/stories/components/CioPlp/CioPlpProps.md +++ b/src/stories/components/CioPlp/CioPlpProps.md @@ -14,7 +14,7 @@ ItemFieldGetters maps the fields sent in the catalog feeds to the fields the lib | ----------- | ------------------------ | ---------------------- | | getPrice | `(item: Item) => number` | Get price funciton | | getImageUrl | `(item: Item) => string` | Get image url funciton | -| getUrl | `(item: Item) => stirng` | Get href url funciton | +| getItemUrl | `(item: Item) => stirng` | Get href url funciton | | getName | `(item: Item) => string` | Get item name funciton | ### `Callbacks` diff --git a/src/types.ts b/src/types.ts index 81594b03..90dab2b9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,11 +39,11 @@ export interface ApiHierarchicalFacetOption extends ApiFacetOption { export type CioClientOptions = Omit; export interface ItemFieldGetters { - getPrice: (item: Item | Variation) => number; - getUrl: (item: Item | Variation) => string; - getImageUrl: (item: Item | Variation) => string; + getPrice: (item: Item | Variation) => number | undefined; + getItemUrl: (item: Item | Variation) => string | undefined; + getImageUrl: (item: Item | Variation) => string | undefined; getName: (item: Item | Variation) => string; - getSwatchPreview: (variation: Variation) => string; + getSwatchPreview: (variation: Variation) => string | undefined; getSwatches: ( item: Item, retrievePrice: ItemFieldGetters['getPrice'], diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index fe27ab72..371d0c39 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -20,7 +20,7 @@ export function getImageUrl( } // eslint-disable-next-line import/prefer-default-export -export function getUrl( +export function getItemUrl( item: Item | Variation, selectedSwatch?: SwatchItem | undefined, ): string | undefined { @@ -31,7 +31,7 @@ export function getUrl( export function getName( item: Item | Variation, selectedSwatch?: SwatchItem | undefined, -): string | undefined { +): string { return selectedSwatch?.itemName || selectedSwatch?.variation?.itemName || item.itemName; } From 1bb5910f690a4a88c17c1d7ca8fd8697e14b2adf Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Thu, 17 Apr 2025 09:41:58 -0700 Subject: [PATCH 05/13] lint --- src/utils/itemFieldGetters.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index 371d0c39..7f9766d7 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -28,10 +28,7 @@ export function getItemUrl( } // eslint-disable-next-line import/prefer-default-export -export function getName( - item: Item | Variation, - selectedSwatch?: SwatchItem | undefined, -): string { +export function getName(item: Item | Variation, selectedSwatch?: SwatchItem | undefined): string { return selectedSwatch?.itemName || selectedSwatch?.variation?.itemName || item.itemName; } From 8d5a90c1d2e1d51477a0a31fb15bfe7fd71132f5 Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Thu, 17 Apr 2025 09:44:58 -0700 Subject: [PATCH 06/13] types update --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 90dab2b9..0791e399 100644 --- a/src/types.ts +++ b/src/types.ts @@ -43,7 +43,7 @@ export interface ItemFieldGetters { getItemUrl: (item: Item | Variation) => string | undefined; getImageUrl: (item: Item | Variation) => string | undefined; getName: (item: Item | Variation) => string; - getSwatchPreview: (variation: Variation) => string | undefined; + getSwatchPreview: (variation: Variation) => string; getSwatches: ( item: Item, retrievePrice: ItemFieldGetters['getPrice'], From ca14ebb8ba81b15ae68e9d2285d7684aa4e7c797 Mon Sep 17 00:00:00 2001 From: stanlp1 Date: Thu, 15 May 2025 22:54:15 -0700 Subject: [PATCH 07/13] separate swatch and product info hook --- src/components/ProductCard/ProductCard.tsx | 7 +++++-- src/hooks/useProduct.ts | 17 ++++++++++------- src/types.ts | 1 - 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx index fef24233..38d55a6c 100644 --- a/src/components/ProductCard/ProductCard.tsx +++ b/src/components/ProductCard/ProductCard.tsx @@ -4,6 +4,7 @@ import { useOnAddToCart, useOnProductCardClick } from '../../hooks/callbacks'; import { IncludeRenderProps, Item, ProductInfoObject } from '../../types'; import ProductSwatch from '../ProductSwatch'; import useProductInfo from '../../hooks/useProduct'; +import useProductSwatch from '../../hooks/useProductSwatch'; interface Props { /** @@ -50,8 +51,10 @@ export type ProductCardProps = IncludeRenderProps export default function ProductCard(props: ProductCardProps) { const { item, children } = props; const state = useCioPlpContext(); - const productInfo = useProductInfo({ item }); - const { productSwatch, itemName, itemPrice, itemImageUrl, itemUrl } = productInfo; + const productSwatch = useProductSwatch({ item }); + const { selectedVariation} = productSwatch; + const productInfo = useProductInfo({ item, selectedVariation }); + const { itemName, itemPrice, itemImageUrl, itemUrl } = productInfo; if (!state) { throw new Error('This component is meant to be used within the CioPlp provider.'); diff --git a/src/hooks/useProduct.ts b/src/hooks/useProduct.ts index 421183f1..6b39a32a 100644 --- a/src/hooks/useProduct.ts +++ b/src/hooks/useProduct.ts @@ -3,9 +3,13 @@ import { useCioPlpContext } from './useCioPlpContext'; import { UseProductInfo } from '../types'; import { tryCatchify } from '../utils'; -const useProductInfo: UseProductInfo = ({ item }) => { +interface UseProductInfoArgs { + item: any; + selectedVariation?: any; +} + +const useProductInfo = ({ item, selectedVariation }: UseProductInfoArgs) => { const state = useCioPlpContext(); - const productSwatch = useProductSwatch({ item }); if (!item.data || !item.itemId || !item.itemName) { throw new Error('data, itemId, or itemName are required.'); @@ -16,15 +20,14 @@ const useProductInfo: UseProductInfo = ({ item }) => { const getItemUrl = tryCatchify(state?.itemFieldGetters?.getItemUrl); const getName = tryCatchify(state?.itemFieldGetters?.getName); - const itemName = getName(item, productSwatch?.selectedVariation); - const itemPrice = getPrice(item, productSwatch?.selectedVariation); - const itemImageUrl = getImageUrl(item, productSwatch?.selectedVariation, { + const itemName = getName(item, selectedVariation); + const itemPrice = getPrice(item, selectedVariation); + const itemImageUrl = getImageUrl(item, selectedVariation, { imageBaseUrl: state.customConfigs.imageBaseUrl, }); - const itemUrl = getItemUrl(item, productSwatch?.selectedVariation); + const itemUrl = getItemUrl(item, selectedVariation); return { - productSwatch, itemName, itemPrice, itemImageUrl, diff --git a/src/types.ts b/src/types.ts index 0791e399..92f14f97 100644 --- a/src/types.ts +++ b/src/types.ts @@ -249,7 +249,6 @@ export type UseProductSwatchProps = { export type UseProductSwatch = (props: UseProductSwatchProps) => ProductSwatchObject; export interface ProductInfoObject { - productSwatch: ProductSwatchObject | undefined; itemName: string; itemPrice: number | undefined; itemUrl: string | undefined; From 6d77ca30a77a507e24b72e9db7cd167fc5e12ec9 Mon Sep 17 00:00:00 2001 From: stanlp1 Date: Thu, 15 May 2025 23:02:59 -0700 Subject: [PATCH 08/13] small fixes --- src/components/ProductCard/ProductCard.tsx | 2 +- src/hooks/useProduct.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx index 38d55a6c..22271bdd 100644 --- a/src/components/ProductCard/ProductCard.tsx +++ b/src/components/ProductCard/ProductCard.tsx @@ -52,7 +52,7 @@ export default function ProductCard(props: ProductCardProps) { const { item, children } = props; const state = useCioPlpContext(); const productSwatch = useProductSwatch({ item }); - const { selectedVariation} = productSwatch; + const { selectedVariation } = productSwatch; const productInfo = useProductInfo({ item, selectedVariation }); const { itemName, itemPrice, itemImageUrl, itemUrl } = productInfo; diff --git a/src/hooks/useProduct.ts b/src/hooks/useProduct.ts index 6b39a32a..18dd1693 100644 --- a/src/hooks/useProduct.ts +++ b/src/hooks/useProduct.ts @@ -1,11 +1,10 @@ -import useProductSwatch from './useProductSwatch'; import { useCioPlpContext } from './useCioPlpContext'; -import { UseProductInfo } from '../types'; import { tryCatchify } from '../utils'; +import { Item, SwatchItem } from '../types'; interface UseProductInfoArgs { - item: any; - selectedVariation?: any; + item: Item; + selectedVariation?: SwatchItem; } const useProductInfo = ({ item, selectedVariation }: UseProductInfoArgs) => { From e5d13c4492c97a5280d788e269bc9a34725ca257 Mon Sep 17 00:00:00 2001 From: stanlp1 Date: Thu, 15 May 2025 23:08:47 -0700 Subject: [PATCH 09/13] update product info test --- .../useProductInfo/useProductInfo.test.js | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/spec/hooks/useProductInfo/useProductInfo.test.js b/spec/hooks/useProductInfo/useProductInfo.test.js index ff4e809a..f93710ac 100644 --- a/spec/hooks/useProductInfo/useProductInfo.test.js +++ b/spec/hooks/useProductInfo/useProductInfo.test.js @@ -30,7 +30,6 @@ describe('Testing Hook: useProductInfo', () => { current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, } = result; - expect(productSwatch).not.toBeNull(); expect(itemName).toEqual(transformedItem.itemName); expect(itemImageUrl).toEqual(transformedItem.imageUrl); expect(itemUrl).toEqual(transformedItem.url); @@ -54,10 +53,9 @@ describe('Testing Hook: useProductInfo', () => { await waitFor(() => { const { - current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, + current: { itemName, itemImageUrl, itemUrl, itemPrice }, } = result; - expect(productSwatch).not.toBeNull(); expect(itemName).toBeUndefined(); expect(itemImageUrl).toBeUndefined(); expect(itemUrl).toBeUndefined(); @@ -81,14 +79,13 @@ describe('Testing Hook: useProductInfo', () => { await waitFor(() => { const { - current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, + current: { itemName, itemImageUrl, itemUrl, itemPrice }, } = result; expect(itemName).toEqual('override'); expect(itemUrl).toEqual('override'); expect(itemImageUrl).toEqual('override'); expect(itemPrice).toEqual('override'); - expect(productSwatch).not.toBeNull(); }); }); @@ -101,10 +98,9 @@ describe('Testing Hook: useProductInfo', () => { await waitFor(() => { const { - current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, + current: { itemName, itemImageUrl, itemUrl, itemPrice }, } = result; - expect(productSwatch).not.toBeNull(); expect(itemName).toEqual(transformedItem.itemName); expect(itemImageUrl).toEqual(`test.com${transformedItem.imageUrl}`); expect(itemUrl).toEqual(transformedItem.url); @@ -112,23 +108,6 @@ describe('Testing Hook: useProductInfo', () => { }); }); - it('Should return correctly after different variation is selected', async () => { - const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem })); - - await waitFor(() => { - const { - current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, - } = result; - const { selectVariation, swatchList } = productSwatch; - selectVariation(swatchList[1]); - - expect(itemName).toEqual(swatchList[1].itemName); - expect(itemImageUrl).toEqual(swatchList[1].imageUrl || transformedItem.imageUrl); - expect(itemUrl).toEqual(swatchList[1].url || transformedItem.url); - expect(itemPrice).toEqual(swatchList[1].price || transformedItem.data.price); - }); - }); - it('Should return nothing properly with getters that throw errors', async () => { const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }), { initialProps: { @@ -148,14 +127,33 @@ describe('Testing Hook: useProductInfo', () => { await waitFor(() => { const { - current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice }, + current: { itemName, itemImageUrl, itemUrl, itemPrice }, } = result; - expect(productSwatch).not.toBeNull(); expect(itemName).toEqual(transformedItem.itemName); expect(itemImageUrl).toEqual(transformedItem.imageUrl); expect(itemUrl).toEqual(transformedItem.url); expect(itemPrice).toBeUndefined(); }); }); + + it('should merge product info fields with selectedVariation when provided', async () => { + const transformedItem = transformResultItem(mockItem); + + const selectedVariation = { + itemName: 'Variation Name', + price: 123.45, + imageUrl: 'variation-image.jpg', + url: 'variation-url', + }; + const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem, selectedVariation })); + + await waitFor(() => { + const { current: { itemName, itemPrice, itemImageUrl, itemUrl } } = result; + expect(itemName).toEqual('Variation Name'); + expect(itemPrice).toEqual(123.45); + expect(itemImageUrl).toEqual('variation-image.jpg'); + expect(itemUrl).toEqual('variation-url'); + }); + }); }); From a2f256053eaafd6ab262047629de813ca1ebdbeb Mon Sep 17 00:00:00 2001 From: stanlp1 Date: Thu, 15 May 2025 23:15:39 -0700 Subject: [PATCH 10/13] fix tests --- spec/utils.test.tsx | 2 +- src/hooks/useProduct.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/utils.test.tsx b/spec/utils.test.tsx index f98d1e75..b38129be 100644 --- a/spec/utils.test.tsx +++ b/spec/utils.test.tsx @@ -9,7 +9,7 @@ const transformedItem = transformResultItem(mockItem); describe('Testing Utils, getProductCardCnstrcDataAttributes', () => { test('Should return relevant data attributes for Product Card', async () => { - const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem })); + const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem, selectedVariation: { variationId: 'BKT00110DG1733LR', swatchPreview: '#FFFFFF' }})); await waitFor(() => { const dataAttributes = getProductCardCnstrcDataAttributes(result.current); diff --git a/src/hooks/useProduct.ts b/src/hooks/useProduct.ts index 6def6810..efd3dc72 100644 --- a/src/hooks/useProduct.ts +++ b/src/hooks/useProduct.ts @@ -32,7 +32,7 @@ const useProductInfo = ({ item, selectedVariation }: UseProductInfoArgs) => { itemPrice, itemImageUrl, itemUrl, - variationId, + variationId: selectedVariation?.variationId, itemId, }; }; From da1cc961f562a4cca2402e96ee41d669d251f28b Mon Sep 17 00:00:00 2001 From: stanlp1 Date: Thu, 15 May 2025 23:16:38 -0700 Subject: [PATCH 11/13] remove unneeded --- src/utils/itemFieldGetters.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index 7f9766d7..b09b9404 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -1,11 +1,9 @@ import { ItemFieldGetters, Item, SwatchItem, Variation } from '../types'; -// eslint-disable-next-line import/prefer-default-export export function getPrice(item: Item | Variation, selectedSwatch?: SwatchItem | undefined): number { return selectedSwatch?.price || selectedSwatch?.variation?.data?.price || item?.data?.price; } -// eslint-disable-next-line import/prefer-default-export export function getImageUrl( item: Item | Variation, selectedSwatch?: SwatchItem | undefined, @@ -19,7 +17,6 @@ export function getImageUrl( return selectedSwatch?.imageUrl || selectedSwatch?.variation?.imageUrl || item?.imageUrl; } -// eslint-disable-next-line import/prefer-default-export export function getItemUrl( item: Item | Variation, selectedSwatch?: SwatchItem | undefined, @@ -27,7 +24,6 @@ export function getItemUrl( return selectedSwatch?.url || selectedSwatch?.variation?.url || item.url; } -// eslint-disable-next-line import/prefer-default-export export function getName(item: Item | Variation, selectedSwatch?: SwatchItem | undefined): string { return selectedSwatch?.itemName || selectedSwatch?.variation?.itemName || item.itemName; } From 04219d007e1e438f39787ce5e3a8bbefbe7d98df Mon Sep 17 00:00:00 2001 From: stanlp1 Date: Fri, 16 May 2025 00:07:00 -0700 Subject: [PATCH 12/13] update product swatch --- .../useProductInfo/useProductInfo.test.js | 18 ++++-------- .../useProductSwatch/useProductSwatch.test.js | 6 ++-- spec/local_examples/item.json | 2 ++ src/hooks/useProductSwatch.ts | 21 +++++++------- src/stories/components/CioPlp/CioPlpProps.md | 12 ++++---- src/types.ts | 13 ++++----- src/utils/itemFieldGetters.ts | 29 +++++++------------ 7 files changed, 43 insertions(+), 58 deletions(-) diff --git a/spec/hooks/useProductInfo/useProductInfo.test.js b/spec/hooks/useProductInfo/useProductInfo.test.js index 6abf965e..da0b70a7 100644 --- a/spec/hooks/useProductInfo/useProductInfo.test.js +++ b/spec/hooks/useProductInfo/useProductInfo.test.js @@ -140,21 +140,15 @@ describe('Testing Hook: useProductInfo', () => { it('should merge product info fields with selectedVariation when provided', async () => { const transformedItem = transformResultItem(mockItem); + const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem, selectedVariation: transformedItem.variations[0] })); - const selectedVariation = { - itemName: 'Variation Name', - price: 123.45, - imageUrl: 'variation-image.jpg', - url: 'variation-url', - }; - const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem, selectedVariation })); - + console.log(transformedItem.variations[0]) await waitFor(() => { const { current: { itemName, itemPrice, itemImageUrl, itemUrl } } = result; - expect(itemName).toEqual('Variation Name'); - expect(itemPrice).toEqual(123.45); - expect(itemImageUrl).toEqual('variation-image.jpg'); - expect(itemUrl).toEqual('variation-url'); + expect(itemName).toEqual(transformedItem.variations[0].itemName); + expect(itemPrice).toEqual(transformedItem.variations[0].data.price); + expect(itemImageUrl).toEqual(transformedItem.variations[0].imageUrl); + expect(itemUrl).toEqual(transformedItem.variations[0].url); }); }); }); diff --git a/spec/hooks/useProductSwatch/useProductSwatch.test.js b/spec/hooks/useProductSwatch/useProductSwatch.test.js index 88d06ba8..2db34de3 100644 --- a/spec/hooks/useProductSwatch/useProductSwatch.test.js +++ b/spec/hooks/useProductSwatch/useProductSwatch.test.js @@ -18,7 +18,7 @@ describe('Testing Hook: useProductSwatch', () => { }); const transformedItem = transformResultItem(mockItem); - const expectedSwatch = getSwatches(transformedItem, getPrice, getSwatchPreview); + const expectedSwatch = getSwatches(transformedItem, getSwatchPreview); it('Should throw error if called outside of PlpContext', () => { expect(() => renderHook(() => useProductSwatch())).toThrow(); @@ -73,7 +73,7 @@ describe('Testing Hook: useProductSwatch', () => { } = result; expect(typeof selectVariation).toBe('function'); - expect(selectedVariation).toBeUndefined(); + expect(selectedVariation).toBe(transformedItem.variations[0]); expect(swatchList.length).toBe(0); }); }); @@ -101,7 +101,7 @@ describe('Testing Hook: useProductSwatch', () => { } = result; expect(typeof selectVariation).toBe('function'); - expect(selectedVariation).toBeUndefined(); + expect(selectedVariation).toBe(transformedItem.variations[0]); expect(swatchList.length).toBe(0); }); }); diff --git a/spec/local_examples/item.json b/spec/local_examples/item.json index c0c26782..acd94180 100644 --- a/spec/local_examples/item.json +++ b/spec/local_examples/item.json @@ -32,6 +32,8 @@ "variation_id": "BKT00110DG1733LR", "swatchPreview": "#e04062", "price": 90, + "image_url": "https://constructorio-integrations.s3.amazonaws.com/tikus-threads/2022-06-29/Casual-Shirts_Washed-Poplin-Shirts_19145-MTX64_40_category-outfitter.jpg", + "url": "https://constructorio-integrations.s3.amazonaws.com/tikus-threads/2022-06-29/Casual-Shirts_Washed-Poplin-Shirts_19145-MTX64_40_category-outfitter.jpg", "facets": [ { "name": "Color", diff --git a/src/hooks/useProductSwatch.ts b/src/hooks/useProductSwatch.ts index 8ed9eec8..b1436da6 100644 --- a/src/hooks/useProductSwatch.ts +++ b/src/hooks/useProductSwatch.ts @@ -1,43 +1,42 @@ import { useEffect, useState } from 'react'; import { useCioPlpContext } from './useCioPlpContext'; -import { SwatchItem, UseProductSwatch } from '../types'; +import { SwatchItem, UseProductSwatch, Variation } from '../types'; import { getSwatches as defaultGetSwatches, - getPrice as defaultGetPrice, getSwatchPreview as defaultGetSwatchPreview, } from '../utils/itemFieldGetters'; const useProductSwatch: UseProductSwatch = ({ item }) => { - const [selectedVariation, setSelectedVariation] = useState(); + const [selectedVariation, setSelectedVariation] = useState(); const [swatchList, setSwatchList] = useState([]); const state = useCioPlpContext(); const getSwatches = state?.itemFieldGetters?.getSwatches || defaultGetSwatches; - const getPrice = state?.itemFieldGetters?.getPrice || defaultGetPrice; const getSwatchPreview = state?.itemFieldGetters?.getSwatchPreview || defaultGetSwatchPreview; useEffect(() => { if (item?.variations) { try { - setSwatchList(getSwatches(item, getPrice, getSwatchPreview) || []); + let swatches = getSwatches(item, getSwatchPreview); + setSwatchList(swatches || []); } catch (e) { // do nothing } } - }, [item, getSwatches, getPrice, getSwatchPreview]); + }, [item, getSwatches, getSwatchPreview]); useEffect(() => { if (item?.variations) { - const initialSwatch = swatchList?.find((swatch) => swatch?.variationId === item?.variationId); - if (initialSwatch) { - setSelectedVariation(initialSwatch); + const initialVariation = item?.variations?.[0]; + if (initialVariation) { + setSelectedVariation(initialVariation); } } }, [swatchList, item]); - const selectVariation = (swatch: SwatchItem) => { - setSelectedVariation(swatch); + const selectVariation = (variation: Variation) => { + setSelectedVariation(variation); }; return { diff --git a/src/stories/components/CioPlp/CioPlpProps.md b/src/stories/components/CioPlp/CioPlpProps.md index 4b7267aa..d84fc9fe 100644 --- a/src/stories/components/CioPlp/CioPlpProps.md +++ b/src/stories/components/CioPlp/CioPlpProps.md @@ -31,12 +31,12 @@ Callbacks will be composed with the library's internal tracking calls for a give ItemFieldGetters maps the fields sent in the catalog feeds to the fields the libary expects for rendering -| property | type | description | -| ----------- | ------------------------ | ---------------------- | -| getPrice | `(item: Item) => number` | Get price funciton | -| getImageUrl | `(item: Item) => string` | Get image url funciton | -| getItemUrl | `(item: Item) => stirng` | Get href url funciton | -| getName | `(item: Item) => string` | Get item name funciton | +| property | type | description | +| ----------- | -----------------------------------------------| ---------------------- | +| getPrice | `(item: Item, variation: Variation) => number` | Get price funciton | +| getImageUrl | `(item: Item, variation: Variation) => string` | Get image url funciton | +| getItemUrl | `(item: Item, variation: Variation) => stirng` | Get href url funciton | +| getName | `(item: Item, variation: Variation) => string` | Get item name funciton |
diff --git a/src/types.ts b/src/types.ts index e4599ecd..ed702e82 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,14 +39,13 @@ export interface ApiHierarchicalFacetOption extends ApiFacetOption { export type CioClientOptions = Omit; export interface ItemFieldGetters { - getPrice: (item: Item | Variation) => number | undefined; - getItemUrl: (item: Item | Variation) => string | undefined; - getImageUrl: (item: Item | Variation) => string | undefined; - getName: (item: Item | Variation) => string; + getPrice: (item: Item, variation?: Variation) => number | undefined; + getItemUrl: (item: Item, variation?: Variation) => string | undefined; + getImageUrl: (item: Item, variation?: Variation) => string | undefined; + getName: (item: Item, variation?: Variation) => string; getSwatchPreview: (variation: Variation) => string; getSwatches: ( item: Item, - retrievePrice: ItemFieldGetters['getPrice'], retrieveSwatchPreview: ItemFieldGetters['getSwatchPreview'], ) => SwatchItem[] | undefined; } @@ -239,8 +238,8 @@ export type UseSortReturn = { export interface ProductSwatchObject { swatchList: SwatchItem[] | undefined; - selectedVariation: SwatchItem | undefined; - selectVariation: (swatch: SwatchItem) => void; + selectedVariation: Variation | undefined; + selectVariation: (variation: Variation) => void; } export type UseProductSwatchProps = { diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index b09b9404..31e798da 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -1,36 +1,28 @@ import { ItemFieldGetters, Item, SwatchItem, Variation } from '../types'; -export function getPrice(item: Item | Variation, selectedSwatch?: SwatchItem | undefined): number { - return selectedSwatch?.price || selectedSwatch?.variation?.data?.price || item?.data?.price; +export function getPrice(item: Item, variation?: Variation): number { + return variation?.data?.price || item?.data?.price; } -export function getImageUrl( - item: Item | Variation, - selectedSwatch?: SwatchItem | undefined, - options?: any, -): string | undefined { +export function getImageUrl(item: Item, variation?: Variation, options?: any): string | undefined { const { imageBaseUrl } = options; if (imageBaseUrl) { - return `${imageBaseUrl}${selectedSwatch?.imageUrl || selectedSwatch?.variation?.imageUrl || item?.imageUrl}`; + return `${imageBaseUrl}${variation?.imageUrl || item?.imageUrl}`; } - return selectedSwatch?.imageUrl || selectedSwatch?.variation?.imageUrl || item?.imageUrl; + return variation?.imageUrl || item?.imageUrl; } -export function getItemUrl( - item: Item | Variation, - selectedSwatch?: SwatchItem | undefined, -): string | undefined { - return selectedSwatch?.url || selectedSwatch?.variation?.url || item.url; +export function getItemUrl(item: Item, variation?: Variation): string | undefined { + return variation?.url || item.url; } -export function getName(item: Item | Variation, selectedSwatch?: SwatchItem | undefined): string { - return selectedSwatch?.itemName || selectedSwatch?.variation?.itemName || item.itemName; +export function getName(item: Item, variation?: Variation): string { + return variation?.itemName || item.itemName; } export function getSwatches( item: Item, - retrievePrice: ItemFieldGetters['getPrice'], retrieveSwatchPreview: ItemFieldGetters['getSwatchPreview'], ): SwatchItem[] | undefined { const swatchList: SwatchItem[] = []; @@ -42,7 +34,6 @@ export function getSwatches( url: variation?.url || item?.url, imageUrl: variation?.url, variationId: variation?.variationId, - price: retrievePrice(variation), swatchPreview: retrieveSwatchPreview(variation), variation, }); @@ -54,4 +45,4 @@ export function getSwatches( export function getSwatchPreview(variation: Variation): string { return variation?.data?.swatchPreview; -} +} \ No newline at end of file From 1b7d14a0d7fdc0e1fa5cd9998568e3546daa7385 Mon Sep 17 00:00:00 2001 From: stanlp1 Date: Fri, 16 May 2025 00:15:12 -0700 Subject: [PATCH 13/13] lint --- src/hooks/useProductSwatch.ts | 2 +- src/utils/itemFieldGetters.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useProductSwatch.ts b/src/hooks/useProductSwatch.ts index b1436da6..b117d3b4 100644 --- a/src/hooks/useProductSwatch.ts +++ b/src/hooks/useProductSwatch.ts @@ -18,7 +18,7 @@ const useProductSwatch: UseProductSwatch = ({ item }) => { useEffect(() => { if (item?.variations) { try { - let swatches = getSwatches(item, getSwatchPreview); + const swatches = getSwatches(item, getSwatchPreview); setSwatchList(swatches || []); } catch (e) { // do nothing diff --git a/src/utils/itemFieldGetters.ts b/src/utils/itemFieldGetters.ts index 31e798da..c2219a58 100644 --- a/src/utils/itemFieldGetters.ts +++ b/src/utils/itemFieldGetters.ts @@ -45,4 +45,4 @@ export function getSwatches( export function getSwatchPreview(variation: Variation): string { return variation?.data?.swatchPreview; -} \ No newline at end of file +}