-
Notifications
You must be signed in to change notification settings - Fork 14
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(component): new builder paging control component #21
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.html
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,62 @@ | ||
<template lwc:render-mode="light"> | ||
<section | ||
class="limitHitTextSection" | ||
if:true={showMessageForResultsLimit}> | ||
<p>{label.resultsLimitHitText}</p> | ||
</section> | ||
|
||
<nav class="pageControlPart"> | ||
<button | ||
class="slds-m-horizontal_small slds-button slds-button_neutral nav-direction" | ||
disabled={disablePaginationPrevious} | ||
aria-disabled={disablePaginationPrevious} | ||
onclick={handlePaginationPrevious}> | ||
<lightning-icon | ||
icon-name="utility:chevronleft" | ||
size="xx-small" | ||
alternative-text={label.previous}></lightning-icon> | ||
</button> | ||
|
||
<template | ||
for:each={pageNumbers} | ||
for:item="pageNumObj"> | ||
<template if:true={pageNumObj.isRange}> | ||
<div | ||
key={pageNumObj.id} | ||
class="slds-m-horizontal_small range-symbol-container"> | ||
{rangeSymbol} | ||
</div> | ||
</template> | ||
<template if:false={pageNumObj.isRange}> | ||
<template if:true={pageNumObj.isCurrentPage}> | ||
<span | ||
class="slds-m-horizontal_small slds-button slds-button_brand nav-button-current" | ||
key={pageNumObj.id} | ||
aria-current="page"> | ||
{pageNumObj.pageNumber} | ||
</span> | ||
</template> | ||
<template if:false={pageNumObj.isCurrentPage}> | ||
<button | ||
class="slds-m-horizontal_small slds-button slds-button_neutral nav-button" | ||
key={pageNumObj.id} | ||
value={pageNumObj.pageNumber} | ||
onclick={handlePaginationPage}> | ||
{pageNumObj.pageNumber} | ||
</button> | ||
</template> | ||
</template> | ||
</template> | ||
|
||
<button | ||
class="slds-m-horizontal_small slds-button slds-button_neutral nav-direction" | ||
disabled={disablePaginationNext} | ||
aria-disabled={disablePaginationNext} | ||
onclick={handlePaginationNext}> | ||
<lightning-icon | ||
icon-name="utility:chevronright" | ||
size="xx-small" | ||
alternative-text={label.next}></lightning-icon> | ||
</button> | ||
</nav> | ||
</template> |
184 changes: 184 additions & 0 deletions
184
force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js
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,184 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
import { LightningElement, api } from 'lwc'; | ||
import { generatePagesForRange } from './pagingControlHelper'; | ||
import { PAGING_RANGE_SYMBOL, MAX_RESULTS_OFFSET } from './constants'; | ||
import { previous, next, resultsLimitHitText } from './labels'; | ||
import { createSearchFiltersUpdateAction, dispatchAction } from 'commerce/actionApi'; | ||
/** | ||
* @param {*} value The value to check. | ||
* @param {number} min The minimum value that _`value`_ needs to have. | ||
* @returns {boolean} Whether the given _`value`_ is a number greater than _`min`_. | ||
*/ | ||
function isNumber(value, min) { | ||
return typeof value === 'number' && !Number.isNaN(value) && value > min; | ||
} | ||
|
||
/** | ||
* A page object to render the page item. | ||
* @typedef {object} PageItem | ||
* @property {number} id | ||
* Identifier used as the key for the rendering element. | ||
* @property {?number} pageNumber | ||
* Page number | ||
* @property {boolean} isCurrentPage | ||
* Whether this page is the current page. | ||
* @property {boolean} isRange | ||
* Whether this page is a range element. | ||
*/ | ||
|
||
/** | ||
* A builder pagination UI control for any record visualization controls. | ||
*/ | ||
export default class SearchPagingControl extends LightningElement { | ||
static renderMode = 'light'; | ||
|
||
/** | ||
* Current page number. | ||
*/ | ||
@api | ||
currentPageNumber; | ||
|
||
/** | ||
* Number of items per page. | ||
*/ | ||
@api | ||
pageSize; | ||
|
||
/** | ||
* Total number of items. | ||
*/ | ||
@api | ||
totalItemCount; | ||
|
||
/** | ||
* The maximum quantity of numbered pages displayed to the user. | ||
* This includes numbers and range symbol. | ||
*/ | ||
@api | ||
maximumPagesDisplayed; | ||
|
||
/** | ||
* Gets the required i18n labels | ||
* @readonly | ||
* @private | ||
*/ | ||
label = { | ||
previous, | ||
next, | ||
resultsLimitHitText, | ||
}; | ||
get normalizedPageNumber() { | ||
return isNumber(this.currentPageNumber, 1) ? this.currentPageNumber : 1; | ||
} | ||
get normalizedPageSize() { | ||
return isNumber(this.pageSize, 1) ? this.pageSize : 1; | ||
} | ||
get normalizedItemCount() { | ||
return isNumber(this.totalItemCount, 0) ? this.totalItemCount : 0; | ||
} | ||
|
||
/** | ||
* Disable previous page navigation? | ||
* @type {boolean} | ||
* @readonly | ||
* @private | ||
*/ | ||
get disablePaginationPrevious() { | ||
return this.normalizedPageNumber === 1; | ||
} | ||
|
||
/** | ||
* Disable next page navigation? | ||
* @type {boolean} | ||
* @readonly | ||
* @private | ||
*/ | ||
get disablePaginationNext() { | ||
return this.normalizedPageNumber >= this.totalPages; | ||
} | ||
|
||
/** | ||
* only show a message if this is the last page we could possibly show while there are more results due to API limitation: | ||
* true if totalItemCount > 5000 + pageSize and this is the last page (aNumber to 5000+pageSize) | ||
* @type {boolean} | ||
* @readonly | ||
* @private | ||
*/ | ||
get showMessageForResultsLimit() { | ||
const pageSize = this.normalizedPageSize; | ||
return ( | ||
this.normalizedItemCount > MAX_RESULTS_OFFSET + pageSize && | ||
this.normalizedPageNumber >= Math.ceil((MAX_RESULTS_OFFSET + pageSize) / pageSize) | ||
); | ||
} | ||
|
||
/** | ||
* Gets total number of pages. | ||
* @type {number} | ||
* @readonly | ||
* @private | ||
*/ | ||
get totalPages() { | ||
return Math.ceil(this.normalizedItemCount / this.normalizedPageSize); | ||
} | ||
|
||
/** | ||
* Gets page numbers as an array of objects. | ||
* @type {PageItem[]} | ||
* @readonly | ||
* @private | ||
*/ | ||
get pageNumbers() { | ||
const max = isNumber(this.maximumPagesDisplayed, 0) ? this.maximumPagesDisplayed : 5; | ||
return generatePagesForRange(this.normalizedPageNumber, this.totalPages, max); | ||
} | ||
|
||
/** | ||
* Gets the symbol for range symbol. | ||
* @type {string} | ||
* @readonly | ||
* @private | ||
*/ | ||
get rangeSymbol() { | ||
return PAGING_RANGE_SYMBOL; | ||
} | ||
|
||
/** | ||
* Handler for the 'click' event from the previous button. | ||
*/ | ||
handlePaginationPrevious() { | ||
const previousPageNumber = Number(this.currentPageNumber) - 1; | ||
this.dispatchUpdateCurrentPageEvent(previousPageNumber); | ||
} | ||
|
||
/** | ||
* Handler for the 'click' event from the next button. | ||
*/ | ||
handlePaginationNext() { | ||
const nextPageNumber = Number(this.currentPageNumber) + 1; | ||
this.dispatchUpdateCurrentPageEvent(nextPageNumber); | ||
} | ||
|
||
/** | ||
* Handler for the 'click' event from the page number button. | ||
* @param {Event} event The event object | ||
*/ | ||
handlePaginationPage(event) { | ||
let pageNumber = parseInt(event.target.value, 10); | ||
this.dispatchUpdateCurrentPageEvent(pageNumber); | ||
} | ||
|
||
/** | ||
* Dispatch filterChange action on search data provider. | ||
* @param {number} newPageNumber | ||
Check warning on line 179 in force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js GitHub Actions / build (16.x)
Check warning on line 179 in force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js GitHub Actions / build (18.x)
|
||
*/ | ||
dispatchUpdateCurrentPageEvent(newPageNumber) { | ||
dispatchAction(this, createSearchFiltersUpdateAction({ page: newPageNumber })); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js-meta.xml
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,18 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<isExposed>true</isExposed> | ||
<targets> | ||
<target>lightningCommunity__Page</target> | ||
<target>lightningCommunity__Default</target> | ||
</targets> | ||
<masterLabel>Custom Search Paging Control</masterLabel> | ||
<description>Displays pagination control for search page.</description> | ||
<targetConfigs> | ||
<targetConfig targets="lightningCommunity__Default"> | ||
<property name="currentPageNumber" type="String" default="{!Search.Pagination.currentPage}" /> | ||
<property name="pageSize" type="String" default="{!Search.Pagination.totalPages}" /> | ||
<property name="totalItemCount" type="String" default="{!Search.Results.total}" /> | ||
<property name="maximumPagesDisplayed" type="String" default="5" /> | ||
</targetConfig> | ||
</targetConfigs> | ||
</LightningComponentBundle> |
18 changes: 18 additions & 0 deletions
18
force-app/main/default/lwc/builderSearchPagingControl/constants.js
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,18 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
export const PAGING_RANGE_SYMBOL = '...'; | ||
|
||
export const EVENT = { | ||
PAGE_CHANGE_PREVIOUS_EVT: 'pageprevious', | ||
|
||
PAGE_CHANGE_NEXT_EVT: 'pagenext', | ||
|
||
PAGE_CHANGE_GOTOPAGE_EVT: 'pagegoto', | ||
}; | ||
|
||
export const MAX_RESULTS_OFFSET = 5000; |
11 changes: 11 additions & 0 deletions
11
force-app/main/default/lwc/builderSearchPagingControl/labels.js
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,11 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
import previous from '@salesforce/label/c.Search_Results_previous'; | ||
import next from '@salesforce/label/c.Search_Results_next'; | ||
import resultsLimitHitText from '@salesforce/label/c.Search_Results_resultsLimitHitText'; | ||
export { previous, next, resultsLimitHitText }; |
99 changes: 99 additions & 0 deletions
99
force-app/main/default/lwc/builderSearchPagingControl/pagingControlHelper.js
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,99 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
/** | ||
* @typedef {import('./searchPagingControl').PageItem} PageItem | ||
*/ | ||
|
||
/** | ||
* Construct a PageItem object with given parameters and add to the given array. | ||
* @param {PageItem[]} pageObjArray reference array to add the PageItem object | ||
* @param {boolean} isRange is this a range? | ||
* @param {number} [pageNumber] page number | ||
* @param {number} [currentPage] current page number | ||
*/ | ||
function addPageObject(pageObjArray, isRange, pageNumber, currentPage) { | ||
const id = pageObjArray.length; | ||
pageObjArray.push({ | ||
id, | ||
pageNumber, | ||
isRange, | ||
isCurrentPage: pageNumber != null && pageNumber === currentPage, | ||
}); | ||
} | ||
|
||
/** | ||
* @param {number} length length of the result array | ||
* @param {number} start starting number of the range | ||
* @returns {Array<number>} An array filled with given range values | ||
*/ | ||
function getRangeArray(length, start) { | ||
return Array.from( | ||
{ | ||
length, | ||
}, | ||
(v, k) => k + start | ||
); | ||
} | ||
|
||
/** | ||
* @param {number} currentPage current page number | ||
* @param {number} totalPage total number of pages | ||
* @param {number} maxPageButtons max no. of page buttons to show in this page range. | ||
* @returns {PageItem[]} An array of {@link PageItem}s with relevant information | ||
* such as `id`, `pageNumber`, `isCurrentPage`, `isRange` | ||
*/ | ||
export function generatePagesForRange(currentPage, totalPage, maxPageButtons) { | ||
let pages = []; | ||
let balanceLength; | ||
let lastPageNumber; | ||
const pageRange = []; | ||
|
||
balanceLength = maxPageButtons - 2; | ||
if (maxPageButtons > totalPage) { | ||
pages = getRangeArray(totalPage, 1); | ||
} else if (currentPage < balanceLength) { | ||
pages = getRangeArray(balanceLength, 1); | ||
|
||
pages.push(totalPage); | ||
} else if (currentPage > totalPage - balanceLength + 1) { | ||
pages = getRangeArray(balanceLength, totalPage - balanceLength + 1); | ||
|
||
pages.unshift(1); | ||
} else { | ||
balanceLength = maxPageButtons - 4; | ||
|
||
let beginIndex = 0; | ||
const halfRB = balanceLength >> 1; | ||
if (halfRB) { | ||
beginIndex = currentPage - halfRB; | ||
|
||
if (balanceLength === 2) { | ||
beginIndex += 1; | ||
} | ||
} else { | ||
beginIndex = currentPage; | ||
} | ||
pages = getRangeArray(balanceLength, beginIndex); | ||
|
||
pages.unshift(1); | ||
pages.push(totalPage); | ||
} | ||
|
||
pages.forEach((pageNumber) => { | ||
if (lastPageNumber) { | ||
if (pageNumber - lastPageNumber === 2) { | ||
addPageObject(pageRange, false, lastPageNumber + 1, currentPage); | ||
} else if (pageNumber - lastPageNumber !== 1) { | ||
addPageObject(pageRange, true, undefined, undefined); | ||
} | ||
} | ||
addPageObject(pageRange, false, pageNumber, currentPage); | ||
lastPageNumber = pageNumber; | ||
}); | ||
return pageRange; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be
const
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a number of warnings flagged by the GH actions that should also be heeded.