-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(commerce): extract core pagination for plp and search (#3475)
* extract core pagination for plp and search * update pagination on search fulfilled * split tests across describes * bring shared constant up * use shared reducer loaders
- Loading branch information
1 parent
fc563a8
commit 023fa90
Showing
10 changed files
with
340 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
...adless/src/controllers/commerce/pagination/core/headless-core-commerce-pagination.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { | ||
selectPage, | ||
nextPage, | ||
previousPage, | ||
} from '../../../../features/commerce/pagination/pagination-actions'; | ||
import {paginationReducer as commercePagination} from '../../../../features/commerce/pagination/pagination-slice'; | ||
import {fetchProductListing} from '../../../../features/commerce/product-listing/product-listing-actions'; | ||
import {buildMockCommerceEngine, MockCommerceEngine} from '../../../../test'; | ||
import { | ||
buildCorePagination, | ||
Pagination, | ||
} from './headless-core-commerce-pagination'; | ||
|
||
describe('core pagination', () => { | ||
let engine: MockCommerceEngine; | ||
let pagination: Pagination; | ||
const fetchResultsActionCreator = fetchProductListing; | ||
|
||
function initPagination() { | ||
engine = buildMockCommerceEngine(); | ||
|
||
pagination = buildCorePagination(engine, {fetchResultsActionCreator}); | ||
} | ||
|
||
beforeEach(() => { | ||
initPagination(); | ||
}); | ||
|
||
it('adds correct reducers to engine', () => { | ||
expect(engine.addReducers).toBeCalledWith({ | ||
commercePagination, | ||
}); | ||
}); | ||
|
||
it('exposes #subscribe method', () => { | ||
expect(pagination.subscribe).toBeTruthy(); | ||
}); | ||
|
||
describe('#selectPage', () => { | ||
beforeEach(() => { | ||
pagination.selectPage(0); | ||
}); | ||
|
||
it('dispatches #selectPage', () => { | ||
expect(engine.actions).toContainEqual(selectPage(0)); | ||
}); | ||
|
||
it('dispatches #fetchResultsActionCreator', () => { | ||
const action = engine.findAsyncAction(fetchResultsActionCreator.pending); | ||
expect(action).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('#nextPage', () => { | ||
beforeEach(() => { | ||
pagination.nextPage(); | ||
}); | ||
|
||
it('dispatches #nextPage', () => { | ||
expect(engine.actions).toContainEqual(nextPage()); | ||
}); | ||
|
||
it('dispatches #fetchResultsActionCreator', () => { | ||
const action = engine.findAsyncAction(fetchResultsActionCreator.pending); | ||
expect(action).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('#previousPage', () => { | ||
beforeEach(() => { | ||
pagination.previousPage(); | ||
}); | ||
|
||
it('dispatches #previousPage', () => { | ||
expect(engine.actions).toContainEqual(previousPage()); | ||
}); | ||
|
||
it('dispatches #fetchResultsActionCreator', () => { | ||
const action = engine.findAsyncAction(fetchResultsActionCreator.pending); | ||
expect(action).toBeTruthy(); | ||
}); | ||
}); | ||
}); |
108 changes: 108 additions & 0 deletions
108
...es/headless/src/controllers/commerce/pagination/core/headless-core-commerce-pagination.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import {AsyncThunkAction} from '@reduxjs/toolkit'; | ||
import {CommerceEngine} from '../../../../app/commerce-engine/commerce-engine'; | ||
import { | ||
nextPage, | ||
selectPage, | ||
previousPage, | ||
} from '../../../../features/commerce/pagination/pagination-actions'; | ||
import {paginationReducer as commercePagination} from '../../../../features/commerce/pagination/pagination-slice'; | ||
import {loadReducerError} from '../../../../utils/errors'; | ||
import { | ||
Controller, | ||
buildController, | ||
} from '../../../controller/headless-controller'; | ||
|
||
/** | ||
* The `Pagination` controller is responsible for navigating between pages of results in a commerce interface. | ||
*/ | ||
export interface Pagination extends Controller { | ||
/** | ||
* Navigates to a specific page. | ||
* | ||
* @param page - The page to navigate to. | ||
*/ | ||
selectPage(page: number): void; | ||
|
||
/** | ||
* Navigates to the next page. | ||
*/ | ||
nextPage(): void; | ||
|
||
/** | ||
* Navigates to the previous page. | ||
*/ | ||
previousPage(): void; | ||
|
||
/** | ||
* A scoped and simplified part of the headless state that is relevant to the `Pagination` controller. | ||
*/ | ||
state: PaginationState; | ||
} | ||
|
||
export interface PaginationState { | ||
page: number; | ||
perPage: number; | ||
totalCount: number; | ||
totalPages: number; | ||
} | ||
|
||
export type PaginationControllerState = Pagination['state']; | ||
|
||
export interface CorePaginationProps { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
fetchResultsActionCreator: () => AsyncThunkAction<unknown, void, any>; | ||
} | ||
|
||
/** | ||
* @internal | ||
* Creates a `Pagination` controller instance. | ||
* | ||
* @param engine - The headless commerce engine. | ||
* @returns A `Pagination` controller instance. | ||
* */ | ||
export function buildCorePagination( | ||
engine: CommerceEngine, | ||
props: CorePaginationProps | ||
): Pagination { | ||
if (!loadPaginationReducers(engine)) { | ||
throw loadReducerError; | ||
} | ||
const controller = buildController(engine); | ||
const {dispatch} = engine; | ||
|
||
const getState = () => { | ||
return engine.state.commercePagination; | ||
}; | ||
|
||
return { | ||
...controller, | ||
|
||
get state() { | ||
return getState(); | ||
}, | ||
|
||
selectPage(page: number) { | ||
dispatch(selectPage(page)); | ||
dispatch(props.fetchResultsActionCreator()); | ||
}, | ||
|
||
nextPage() { | ||
dispatch(nextPage()); | ||
dispatch(props.fetchResultsActionCreator()); | ||
}, | ||
|
||
previousPage() { | ||
dispatch(previousPage()); | ||
dispatch(props.fetchResultsActionCreator()); | ||
}, | ||
}; | ||
} | ||
|
||
function loadPaginationReducers( | ||
engine: CommerceEngine | ||
): engine is CommerceEngine { | ||
engine.addReducers({ | ||
commercePagination, | ||
}); | ||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 11 additions & 89 deletions
100
...rc/controllers/commerce/product-listing/pagination/headless-product-listing-pagination.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,104 +1,26 @@ | ||
import {CommerceEngine} from '../../../../app/commerce-engine/commerce-engine'; | ||
import { | ||
nextPage, | ||
selectPage, | ||
previousPage, | ||
} from '../../../../features/commerce/pagination/pagination-actions'; | ||
import {paginationReducer as commercePagination} from '../../../../features/commerce/pagination/pagination-slice'; | ||
import {fetchProductListing} from '../../../../features/commerce/product-listing/product-listing-actions'; | ||
import {productListingV2Reducer as productListing} from '../../../../features/commerce/product-listing/product-listing-slice'; | ||
import {loadReducerError} from '../../../../utils/errors'; | ||
import { | ||
Controller, | ||
buildController, | ||
} from '../../../controller/headless-controller'; | ||
buildCorePagination, | ||
Pagination, | ||
} from '../../pagination/core/headless-core-commerce-pagination'; | ||
import {loadProductListingReducer} from '../utils/load-product-listing-reducers'; | ||
|
||
/** | ||
* The `ProductListingPagination` controller is responsible for navigating between pages of results in a commerce product listing interface. | ||
*/ | ||
export interface ProductListingPagination extends Controller { | ||
/** | ||
* Navigates to a specific page. | ||
* | ||
* @param page The page to navigate to. | ||
*/ | ||
selectPage(page: number): void; | ||
|
||
/** | ||
* Navigates to the next page. | ||
*/ | ||
nextPage(): void; | ||
|
||
/** | ||
* Navigates to the previous page. | ||
*/ | ||
previousPage(): void; | ||
|
||
/** | ||
* A scoped and simplified part of the headless state that is relevant to the `ProductListingPagination` controller. | ||
*/ | ||
state: ProductListingPaginationState; | ||
} | ||
|
||
export interface ProductListingPaginationState { | ||
page: number; | ||
perPage: number; | ||
totalCount: number; | ||
totalPages: number; | ||
} | ||
|
||
export type ProductListingPaginationControllerState = | ||
ProductListingPagination['state']; | ||
|
||
/** | ||
* Creates a `ProductListingPagination` controller instance. | ||
* Creates a `Pagination` controller instance. | ||
* | ||
* @param engine - The headless commerce engine. | ||
* @returns A `ProductListingPagination` controller instance. | ||
* @returns A `Pagination` controller instance. | ||
* */ | ||
export function buildProductListingPagination( | ||
engine: CommerceEngine | ||
): ProductListingPagination { | ||
if (!loadProductListingPaginationReducers(engine)) { | ||
): Pagination { | ||
if (!loadProductListingReducer(engine)) { | ||
throw loadReducerError; | ||
} | ||
const controller = buildController(engine); | ||
const {dispatch} = engine; | ||
|
||
const getState = () => { | ||
return engine.state.commercePagination!; | ||
}; | ||
|
||
return { | ||
...controller, | ||
|
||
get state() { | ||
return getState(); | ||
}, | ||
|
||
selectPage(page: number) { | ||
dispatch(selectPage(page)); | ||
dispatch(fetchProductListing()); | ||
}, | ||
|
||
nextPage() { | ||
dispatch(nextPage()); | ||
dispatch(fetchProductListing()); | ||
}, | ||
|
||
previousPage() { | ||
dispatch(previousPage()); | ||
dispatch(fetchProductListing()); | ||
}, | ||
}; | ||
|
||
function loadProductListingPaginationReducers( | ||
engine: CommerceEngine | ||
): engine is CommerceEngine { | ||
engine.addReducers({ | ||
productListing, | ||
commercePagination, | ||
}); | ||
return true; | ||
} | ||
return buildCorePagination(engine, { | ||
fetchResultsActionCreator: fetchProductListing, | ||
}); | ||
} |
Oops, something went wrong.