Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: join relatedCommercedata with editorial data #974

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
106 changes: 106 additions & 0 deletions packages/react/src/products/hooks/__tests__/useProductListing.test.tsx
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 @@ -203,6 +207,41 @@ describe('useProductListing', () => {
});
});

it('should fetch data if `enableAutoFetch` is true, is custom listing page and there is no loaded data', () => {
const slug = getSlug('en/customlistingpage', true);
const state = {
entities: {},
products: {
...mockProductsState.products,
lists: {
error: {},
isHydrated: {},
isLoading: {},
hash: null,
productListingFacets: {
isLoading: false,
error: null,
result: [],
},
},
},
};

renderHook(
() =>
useProductListing(slug, {
isCustomListingPage: true,
}),
{
wrapper: withStore(state),
},
);

expect(fetchProductSet).not.toHaveBeenCalled();
expect(fetchProductListing).not.toHaveBeenCalled();
expect(fetchCustomListing).toHaveBeenCalled();
});

it('should fetch data if `enableAutoFetch` is true and there is no loaded data', () => {
const slug = getSlug('shopping/men/clothing');

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

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

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

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

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 +404,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,
isCustomListingPage: 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;
isCustomListingPage?: 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,
isCustomListingPage = false,
} = options;

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

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,
)
: isCustomListingPage
? fetchCustomListingAction(
slug,
options.query,
{ useCache, setProductsListHash },
fetchConfig,
)
: fetchListingAction(
slug,
options.query,
Expand All @@ -99,6 +110,8 @@ const useProductListing = (
setProductsListHash,
slug,
useCache,
isCustomListingPage,
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,
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
boliveira marked this conversation as resolved.
Show resolved Hide resolved
* custom listing page by its id.
*
* @param getProductSet - Get custom listing page produts 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 isCustomListingPage - 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,
isCustomListingPage: boolean,
): Promise<ProductListing | ProductSet | undefined> => {
let hash: Nullable<string> = null;

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

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.
isCustomListingPage ? '' : 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,
boliveira marked this conversation as resolved.
Show resolved Hide resolved
);

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
Loading