Skip to content

Commit

Permalink
feat: join relatedCommercedata with editorial data
Browse files Browse the repository at this point in the history
  • Loading branch information
edupaulos committed Jan 29, 2024
1 parent 5bb8b29 commit 796ab89
Show file tree
Hide file tree
Showing 23 changed files with 939 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
fetchCustomListing,
fetchProductListing,
fetchProductSet,
getSlug,
Expand All @@ -7,6 +8,8 @@ import {
import { mockBrandResponse } from 'tests/__fixtures__/brands/index.mjs';
import { mockCategory } from 'tests/__fixtures__/categories/index.mjs';
import {
mockCustomListingPageHash,
mockCustomListingPageState,
mockProductsListDenormalizedFacetGroups,
mockProductsListHash,
mockProductsListHashWithoutParameters,
Expand All @@ -25,6 +28,7 @@ jest.mock('@farfetch/blackout-redux', () => ({
...jest.requireActual('@farfetch/blackout-redux'),
fetchProductListing: jest.fn(() => () => Promise.resolve()),
fetchProductSet: jest.fn(() => () => Promise.resolve()),
fetchCustomListing: jest.fn(() => () => Promise.resolve()),
resetProductListings: jest.fn(() => () => Promise.resolve()),
}));

Expand Down Expand Up @@ -249,6 +253,47 @@ describe('useProductListing', () => {
},
});
});

it('should return data correctly when `isACustomListingPage` is true', () => {
const slug = getSlug('en/customlistingpage', true);

const { result } = renderHook(
() => useProductListing(slug, { isACustomListingPage: true }),
{
wrapper: withStore(mockCustomListingPageState),
},
);

const mockList =
mockProductsListNormalizedPayload.entities.productsLists[
mockCustomListingPageHash
];

expect(fetchCustomListing).not.toHaveBeenCalled();

expect(result.current).toStrictEqual({
error: undefined,
isFetched: true,
isLoading: false,
data: {
...mockList,
facetGroups:
mockProductsListDenormalizedFacetGroups[mockCustomListingPageHash],
hash: mockCustomListingPageHash,
items: expectedProductsDenormalized,
pagination: {
number: mockList.products.number,
pageSize: mockList.config.pageSize,
totalItems: mockList.products.totalItems,
totalPages: mockList.products.totalPages,
},
},
actions: {
reset: expect.any(Function),
refetch: expect.any(Function),
},
});
});
});

describe('actions', () => {
Expand Down Expand Up @@ -326,5 +371,33 @@ describe('useProductListing', () => {
undefined,
);
});

it('should call `fetchCustomListing` successfully when `refetch` action is called and type is `listing`', () => {
const {
result: {
current: {
actions: { refetch },
},
},
} = renderHook(
() =>
useProductListing(slug, {
useCache: false,
isACustomListingPage: true,
}),
{
wrapper: withStore(mockCustomListingPageState),
},
);

refetch();

expect(fetchCustomListing).toHaveBeenCalledWith(
slug,
undefined,
{ setProductsListHash: undefined, useCache: false },
undefined,
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface UseProductListingCommonOptions {
useCache?: boolean;
enableAutoFetch?: boolean;
setProductsListHash?: boolean;
isACustomListingPage?: boolean;
fetchConfig?: Config;
}

Expand Down
13 changes: 13 additions & 0 deletions packages/react/src/products/hooks/useProductListing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
fetchCustomListing,
fetchProductListing,
fetchProductSet,
generateProductListingHash,
Expand Down Expand Up @@ -39,15 +40,18 @@ const useProductListing = (
useCache = true,
setProductsListHash,
enableAutoFetch = true,
isACustomListingPage = false,
} = options;

const isSetPage = isUseProductListingTypeSetOptions(options);
const productListingHash = generateProductListingHash(slug, query, {
isSet: isSetPage,
isACustomListingPage,
});

const fetchListingAction = useAction(fetchProductListing);
const fetchSetAction = useAction(fetchProductSet);
const fetchCustomListingAction = useAction(fetchCustomListing);
const resetAction = useAction(resetProductListings);
const reset = useCallback(() => {
resetAction([productListingHash]);
Expand Down Expand Up @@ -84,6 +88,13 @@ const useProductListing = (
{ useCache, setProductsListHash },
fetchConfig,
)
: isACustomListingPage
? fetchCustomListingAction(
slug,
options.query,
{ useCache, setProductsListHash },
fetchConfig,
)
: fetchListingAction(
slug,
options.query,
Expand All @@ -99,6 +110,8 @@ const useProductListing = (
setProductsListHash,
slug,
useCache,
isACustomListingPage,
fetchCustomListingAction,
],
);

Expand Down
2 changes: 2 additions & 0 deletions packages/redux/src/__tests__/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,8 @@ Object {
"fetchCountryStateCitiesFactory": [Function],
"fetchCountryStates": [Function],
"fetchCountryStatesFactory": [Function],
"fetchCustomListing": [Function],
"fetchCustomListingFactory": [Function],
"fetchExchange": [Function],
"fetchExchangeBookRequest": [Function],
"fetchExchangeBookRequestFactory": [Function],
Expand Down
14 changes: 6 additions & 8 deletions packages/redux/src/contents/serverInitialState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@ const serverInitialState: ServerInitialState = ({ model }) => {
const { searchContentRequests, slug, seoMetadata, subfolder } = model;

const contents = searchContentRequests.reduce((acc, item) => {
const { searchResponse } = item;
const firstSearchResponseItem = searchResponse.entries[0];

if (!firstSearchResponseItem) {
return acc;
}
const {
filters: { codes, contentTypeCode },
searchResponse,
} = item;

const hash = generateContentHash({
codes: firstSearchResponseItem.code,
contentTypeCode: firstSearchResponseItem.contentTypeCode,
codes: codes,
contentTypeCode: contentTypeCode,
});

const { entities, result } = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import fetchProductListFactory from './fetchProductListFactory.js';
import type {
Config,
GetProductListing,
GetProductListingQuery,
ProductListing,
} from '@farfetch/blackout-client';
import type { Dispatch } from 'redux';
import type { GetOptionsArgument, StoreState } from '../../../types/index.js';
import type { ProductListActionOptions } from '../../types/index.js';

/**
* Creates a thunk factory configured with the specified client to fetch a specific
* set by its id.
*
* @param getProductSet - Get product set client.
*
* @returns Thunk factory.
*/
const fetchCustomListingFactory =
(getListing: GetProductListing) =>
(
slug: string | number,
query: GetProductListingQuery = {},
actionOptions?: ProductListActionOptions,
config?: Config,
) =>
(
dispatch: Dispatch,
getState: () => StoreState,
options: GetOptionsArgument,
): Promise<ProductListing | undefined> =>
fetchProductListFactory(
getListing,
slug,
query,
actionOptions,
config,
dispatch,
getState,
options,
false,
true,
) as Promise<ProductListing | undefined>;

export default fetchCustomListingFactory;
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ import type { ProductListActionOptions } from '../../types/index.js';
* Creates a thunk configured with the specified client to fetch a product listing
* for a given slug with specific query parameters.
*
* @param client - Get listing or sets client.
* @param slug - Slug to load product list for.
* @param query - Query parameters to apply.
* @param actionOptions - Additional options to apply to the action.
* @param config - Custom configurations to send to the client instance (axios).
* @param dispatch - Redux dispatch.
* @param getState - Store state.
* @param options - Thunk options.
* @param isSet - If is sets scope or not.
* @param client - Get listing or sets client.
* @param slug - Slug to load product list for.
* @param query - Query parameters to apply.
* @param actionOptions - Additional options to apply to the action.
* @param config - Custom configurations to send to the client instance (axios).
* @param dispatch - Redux dispatch.
* @param getState - Store state.
* @param options - Thunk options.
* @param isSet - If is sets scope or not.
* @param isACustomListingPage - If is custom listing page scope or not.
*
* @returns Thunk to be dispatched to the redux store.
*/
Expand All @@ -55,11 +56,15 @@ const fetchProductListFactory = async (
getOptions = arg => ({ productImgQueryParam: arg.productImgQueryParam }),
}: GetOptionsArgument,
isSet: boolean,
isACustomListingPage: boolean,
): Promise<ProductListing | ProductSet | undefined> => {
let hash: Nullable<string> = null;

try {
hash = generateProductListingHash(slug, query, { isSet });
hash = generateProductListingHash(slug, query, {
isSet,
isACustomListingPage,
});

const { productImgQueryParam } = getOptions(getState);
const isHydrated = isProductListingHydrated(getState(), hash);
Expand Down Expand Up @@ -108,8 +113,12 @@ const fetchProductListFactory = async (
type: actionTypes.FETCH_PRODUCT_LISTING_REQUEST,
});

// @ts-expect-error Property slug can be a string or a number.
const result = await client(slug, query, config);
const result = await client(
// @ts-expect-error Property slug can be a string or a number.
isACustomListingPage ? '' : slug,
query,
config,
);

dispatch({
meta: { hash },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const fetchProductListingFactory =
getState,
options,
false,
false,
);

export default fetchProductListingFactory;
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const fetchProductSetFactory =
getState,
options,
true,
false,
) as Promise<ProductSet | undefined>;

export default fetchProductSetFactory;
1 change: 1 addition & 0 deletions packages/redux/src/products/actions/factories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { default as fetchProductListingFactory } from './fetchProductListingFact
export { default as fetchProductMeasurementsFactory } from './fetchProductMeasurementsFactory.js';
export { default as fetchProductOutfitsFactory } from './fetchProductOutfitsFactory.js';
export { default as fetchProductSetFactory } from './fetchProductSetFactory.js';
export { default as fetchCustomListingFactory } from './fetchCustomListingFactory.js';
export { default as fetchProductSizeGuidesFactory } from './fetchProductSizeGuidesFactory.js';
export { default as fetchProductSizesFactory } from './fetchProductSizesFactory.js';
export { default as fetchProductVariantsByMerchantsLocationsFactory } from './fetchProductVariantsByMerchantsLocationsFactory.js';
Expand Down
7 changes: 7 additions & 0 deletions packages/redux/src/products/actions/fetchCustomListing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { fetchCustomListingFactory } from './factories/index.js';
import { getProductListing } from '@farfetch/blackout-client';

/**
* Fetch a specific custom listing by its id.
*/
export default fetchCustomListingFactory(getProductListing);
1 change: 1 addition & 0 deletions packages/redux/src/products/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export { default as fetchProductListing } from './fetchProductListing.js';
export { default as fetchProductMeasurements } from './fetchProductMeasurements.js';
export { default as fetchProductOutfits } from './fetchProductOutfits.js';
export { default as fetchProductSet } from './fetchProductSet.js';
export { default as fetchCustomListing } from './fetchCustomListing.js';
export { default as fetchProductSizeGuides } from './fetchProductSizeGuides.js';
export { default as fetchProductSizes } from './fetchProductSizes.js';
export { default as fetchProductVariantsByMerchantsLocations } from './fetchProductVariantsByMerchantsLocations.js';
Expand Down
Loading

0 comments on commit 796ab89

Please sign in to comment.