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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions spec/hooks/useProductInfo/useProductInfo.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ describe('Testing Hook: useProductInfo', () => {

const transformedItem = transformResultItem(mockItem);

it('Should return productSwatch, itemName, itemImageUrl, itemUrl, itemPrice', async () => {
it('Should return productSwatch, itemId, itemName, itemImageUrl, itemUrl, itemPrice', async () => {
const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }));

await waitFor(() => {
const {
current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice },
current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice, itemId },
} = result;

expect(productSwatch).not.toBeNull();
expect(itemId).toEqual(transformedItem.itemId);
expect(itemName).toEqual(transformedItem.itemName);
expect(itemImageUrl).toEqual(transformedItem.imageUrl);
expect(itemUrl).toEqual(transformedItem.url);
Expand Down
25 changes: 25 additions & 0 deletions spec/utils.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { waitFor } from '@testing-library/react';
import { transformResultItem } from '../src/utils/transformers';
import { getProductCardCnstrcDataAttributes } from '../src/utils';
import useProductInfo from '../src/hooks/useProduct';
import mockItem from './local_examples/item.json';
import { renderHookWithCioPlp } from './test-utils';

const transformedItem = transformResultItem(mockItem);

describe('Testing Utils, getProductCardCnstrcDataAttributes', () => {
test('Should return relevant data attributes for Product Card', async () => {
const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }));

await waitFor(() => {
const dataAttributes = getProductCardCnstrcDataAttributes(result.current);

expect(dataAttributes['data-cnstrc-item-id']).toBe('KNITS00423-park bench dot');
expect(dataAttributes['data-cnstrc-item-name']).toBe(
'Jersey Riviera Shirt (Red Park Bench Dot)',
);
expect(dataAttributes['data-cnstrc-item-price']).toBe(90);
expect(dataAttributes['data-cnstrc-item-variation-id']).toBe('BKT00110DG1733LR');
});
});
});
11 changes: 10 additions & 1 deletion src/components/ProductCard/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import { useCioPlpContext } from '../../hooks/useCioPlpContext';
import { useOnAddToCart, useOnProductCardClick } from '../../hooks/callbacks';
import { IncludeRenderProps, Item, ProductInfoObject } from '../../types';
import { CnstrcData, IncludeRenderProps, Item, ProductInfoObject } from '../../types';
import ProductSwatch from '../ProductSwatch';
import useProductInfo from '../../hooks/useProduct';
import { getProductCardCnstrcDataAttributes } from '../../utils';

interface Props {
/**
Expand Down Expand Up @@ -40,6 +41,10 @@ export interface ProductCardRenderProps extends ProductCardProps {
* Set globally at the CioPlp provider level.
*/
onClick: (event: React.MouseEvent, item: Item) => void;
/**
* Data Attributes to surface on parent div of product card.
*/
productCardCnstrcDataAttributes: CnstrcData;
}

export type ProductCardProps = IncludeRenderProps<Props, ProductCardRenderProps>;
Expand All @@ -66,6 +71,8 @@ export default function ProductCard(props: ProductCardProps) {
const { formatPrice } = state.formatters;
const onClick = useOnProductCardClick(client, state.callbacks.onProductCardClick);

const cnstrcData = getProductCardCnstrcDataAttributes(productInfo);

return (
<>
{typeof children === 'function' ? (
Expand All @@ -75,9 +82,11 @@ export default function ProductCard(props: ProductCardProps) {
formatPrice,
onAddToCart,
onClick,
productCardCnstrcDataAttributes: cnstrcData,
})
) : (
<a
{...cnstrcData}
className='cio-product-card'
href={itemUrl}
onClick={(e) => onClick(e, item, productSwatch?.selectedVariation?.variationId)}>
Expand Down
4 changes: 4 additions & 0 deletions src/hooks/useProduct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ const useProductInfo: UseProductInfo = ({ item }) => {
const itemPrice = productSwatch?.selectedVariation?.price || getPrice(item);
const itemImageUrl = productSwatch?.selectedVariation?.imageUrl || item.imageUrl;
const itemUrl = productSwatch?.selectedVariation?.url || item.url;
const variationId = productSwatch?.selectedVariation?.variationId;
const { itemId } = item;

return {
productSwatch,
itemName,
itemPrice,
itemImageUrl,
itemUrl,
variationId,
itemId,
};
};

Expand Down
12 changes: 8 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,13 @@ export type UseProductSwatchProps = {
export type UseProductSwatch = (props: UseProductSwatchProps) => ProductSwatchObject;

export interface ProductInfoObject {
productSwatch: ProductSwatchObject | undefined;
productSwatch?: ProductSwatchObject;
itemName: string;
itemPrice: number | undefined;
itemUrl: string | undefined;
itemImageUrl: string | undefined;
itemId: string;
itemPrice?: number;
itemUrl?: string;
itemImageUrl?: string;
variationId?: string;
}

export type UseProductInfoProps = {
Expand Down Expand Up @@ -308,6 +310,8 @@ export interface PlpItemGroup {
parents: Pick<PlpItemGroup, 'groupId' | 'displayName'>[];
}

export type CnstrcData = Record<`data-cnstrc-${string}`, string | number | boolean>;

// Type Extenders
export type PropsWithChildren<P> = P & { children?: ReactNode };

Expand Down
63 changes: 63 additions & 0 deletions src/utils/dataAttributeHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
RequestConfigs,
PlpSearchDataResults,
PlpSearchDataRedirect,
PlpBrowseData,
CnstrcData,
ProductInfoObject,
} from '../types';
import { isPlpBrowseDataResults, isPlpSearchDataResults } from './typeHelpers';
import { getPageType } from './requestConfigsHelpers';

export function getProductCardCnstrcDataAttributes(productInfo: ProductInfoObject) {
let dataCnstrc: CnstrcData = {};

const { itemId, itemPrice, itemName, variationId } = productInfo;

dataCnstrc = {
'data-cnstrc-item-id': itemId,
'data-cnstrc-item-name': itemName,
'data-cnstrc-item-price': itemPrice!,
'data-cnstrc-item-variation-id': variationId!,
};

return dataCnstrc;
}

export function getPlpContainerCnstrcDataAttributes(
data: PlpSearchDataResults | PlpSearchDataRedirect | PlpBrowseData | null,
requestConfigs: RequestConfigs,
) {
if (!data || (!isPlpSearchDataResults(data) && !isPlpBrowseDataResults(data))) return {};

const { filterName, filterValue } = requestConfigs;
const pageType = getPageType(requestConfigs);
let dataCnstrc: Record<`data-cnstrc-${string}`, string | number | boolean> = {};

switch (pageType) {
case 'browse':
dataCnstrc = {
'data-cnstrc-browse': true,
'data-cnstrc-num-results': data.response.totalNumResults,
'data-cnstrc-filter-name': filterName!,
'data-cnstrc-filter-value': filterValue!,
};
break;

case 'search':
dataCnstrc = {
'data-cnstrc-search': true,
'data-cnstrc-num-results': data.response.totalNumResults,
};
break;

case 'unknown':
dataCnstrc = {};
break;

default:
break;
}

return dataCnstrc;
}
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './itemFieldGetters';
export * from './styleHelpers';
export * from './transformers';
export * from './typeHelpers';
export * from './dataAttributeHelpers';
export * from './urlHelpers';
export * from './requestConfigsHelpers';
export * from './common';
47 changes: 1 addition & 46 deletions src/utils/requestConfigsHelpers.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { SearchParameters } from '@constructor-io/constructorio-client-javascript/lib/types';
import {
RequestConfigs,
RequestQueryParams,
PlpSearchDataResults,
PlpSearchDataRedirect,
PlpBrowseData,
} from '../types';
import { RequestConfigs, RequestQueryParams } from '../types';
import { removeNullValuesFromObject } from './common';
import { isPlpBrowseDataResults, isPlpSearchDataResults } from './typeHelpers';

export function getSearchParamsFromRequestConfigs(requestConfigs: RequestConfigs): {
query: string;
Expand Down Expand Up @@ -52,41 +45,3 @@ export function getPageType(requestConfigs: RequestConfigs): PageType {
}
return 'unknown';
}

export function getPlpContainerCnstrcDataAttributes(
data: PlpSearchDataResults | PlpSearchDataRedirect | PlpBrowseData | null,
requestConfigs: RequestConfigs,
) {
if (!data || (!isPlpSearchDataResults(data) && !isPlpBrowseDataResults(data))) return {};

const { filterName, filterValue } = requestConfigs;
const pageType = getPageType(requestConfigs);
let dataCnstrc: Record<`data-cnstrc-${string}`, string | number | boolean> = {};

switch (pageType) {
case 'browse':
dataCnstrc = {
'data-cnstrc-browse': true,
'data-cnstrc-num-results': data.response.totalNumResults,
'data-cnstrc-filter-name': filterName!,
'data-cnstrc-filter-value': filterValue!,
};
break;

case 'search':
dataCnstrc = {
'data-cnstrc-search': true,
'data-cnstrc-num-results': data.response.totalNumResults,
};
break;

case 'unknown':
dataCnstrc = {};
break;

default:
break;
}

return dataCnstrc;
}
Loading