We need to build a common paginator component for Terra.
New Feature
Render a paginator component from provided props, in accordance with UX designs.
Forward/Back: URL, start, limit, startParam, limitParam
First/Last: Total count
Paged: Page selected
General: Variant
The basic proposed functionality is the forward/back option. This requires a base URL and the key/values associate with start and limit. I'm taking the params as props because we can't guarantee that all consumers will use start/limit for their query params.
The first/last functionality is dependent on knowing how many pages exist, which can be calculate with the limit and total count.
Listing the page numbers is also dependent on knowing how many pages exist, but additionally needs to know currently selected page to build the relative linking and selected-page-display.
The variant will allow the consumer to flex between the Search Paginator and the Progressive Paginator. The progressive paginator will additionally be dependent on selected page as a "required" prop.
<Paginator
baseUri={base_uri_for_pagination}
startParam={some_start_param}
limitParam={some_limit_param}
start={some_start}
limit={some_limit}
page={selected_page} // Optional
totalCount={totalCount} // Optional
// onPageChange is intended to support optional Ajax loading of content on pagination.
onPageChange={callbackToLoadContentToContainer()} // Optional
variant={‘search’ || ‘progressive’} // Hate these names. Don’t have better ones. Optional. Defaults ‘search’.
/>
var isPaged = (totalCount, page) => (!(typeof(totalCount) === 'undefined' || typeof(page) === 'undefined'));
var getPageCount = (limit, totalCount) => (Math.ceil(totalCount / limit));
// May redesign into a more complex function to account for more situations and be a little more readable.
// Ex: Setting previous to undefined if previous would result in a negative page.
var urlBuilder = (baseUrl, startParam, limitParam, start, limit, totalCount = undefined, page = undefined) => (
{
href: baseUrl,
next: `${baseUrl}?${startParam}=${start + limit}&${limitParam}=${limit}` + (page ? `page=${page + 1}` : ''),
previous: `${baseUrl}?${startParam}=${start - limit}&${limitParam}=${limit}` + (page ? `page=${page - 1}` : ''),
first: totalCount ? `${baseUrl}?${startParam}=0&${limitParam}=${limit}&page=1` : undefined,
last: totalCount ? `${baseUrl}?${startParam}=${getPageCount(limit, totalCount) - limit}&${limitParam}=${limit}&page=${getPageCount(limit, totalCount)}` : undefined,
// The 'page' property below may be pulled out and built in the main component, or I might parse and modify as-needed.
page: isPaged(totalCount, page) ? `${baseUrl}?${startParam}=${start}&${limitParam}=${limit}&page=${page}` : undefined,
}
);
export default urlBuilder;
Let's rename the page prop to currentPage so that it's more clear as to it's intent.
@neilpfeiffer Some of our old pagination used show more style paginators that didn't know how many pages existed. Do we need to account for scenarios when we are unsure of how many total pages there are?
How about themability variables? We have posted them on several tech designs.
Also, should we do these as two different paginators instead of using a prop to switch between the two?
@neilpfeiffer Some of our old pagination used show more style paginators that didn't know how many pages existed. Do we need to account for scenarios when we are unsure of how many total pages there are?
The various components (like page numbers) are listed as conditional at the top of the basic implementation. I took this to mean that the component should flex based on what information is provided.
Also, should we do these as two different paginators instead of using a prop to switch between the two?
Is there a technical/UX/whatever reason you think that splitting them into separate top-level components is better than just providing a variant that achieves basically the same thing?
If props or other aspects change for the API on one paginator but not on the other, it's easier to adjust for. Otherwise, we could end up having a large paginator component that has a large set of props for which only half are used at a given time.
Is that a realistic possibility, though? Sure, the design could change, but that is true for literally all of our components. Having said that, I know the updates to button caused a bunch of headaches for everyone involved.
@bjankord: Thoughts?
The above tech design is out-of-date per discussion last week. Rather than providing a mechanism to build and manage URLs in-component, we will require the user to pass in a callback function that has the Ajax/page-reload logic on their end.
A new formal design will be forthcoming.
Toggling between Search and Progressive pagination will be done by explicitly importing the desired paginator.
<Paginator
onPageChange={my_callback_function}
selectedPage={selected_page}
totalCount={total_count}
itemCountPerPage={number_of_items_per_page}
/>
const handlePageChange = (event, selectedPage) => {
switch (selectedPage) {
case typeof(parseInt(selectedPage)) === 'number':
window.location.href = "http://www.terra-ui.com";
case 'previous':
// Set window.location to previous.
case 'next':
// Set window.location to next.
case 'first':
// Set window.location to first.
case 'last':
// Set window.location to last.
default:
return null;
}
}
If your goal is to use async calls to reload content, just replace the window.location code with whatever async call you need.
Disclaimer: Pseudocode
Paginator = (props) => {
this.handleOnChange = (event, selectedPage) => {
if (this.props.onPageChange) {
this.props.onPageChange(event, selectedPage)I
}
if (this.props.selectedPage && this.props.selectedPage !== selectedPage) {
this.setState({ selectedPage: selectedPage })
}
}
render() {
<div className={cx('paginator')}>{...}</div>
}
}
Theme support will be provided to customize the following attributes:
+1 on tech design
+1 on tech design
+1 on tech design
+1 on tech design
Get get the page number to display, will the selectedPage, totalCount, and itemCountPerPage all need to be provided? Also, will itemCountPerPage have a default?
I say for now that we make all those props required. As for default itemCountPerPage, I think different teams across Cerner had different values for that, so I wouldn't default that for now.
They shouldn't be required because the design accounts for showing fewer UI elements (just next/previous) when page number isn't known.
I could see having totalCount and itemCountPerPage required, but not 100% sold on it.
Sorry. I was vague. I meant requiring them all in order to get the display. I'm fine with leaving them as optional with the descriptions you provided.
Going with the v2 design for my PR.
JIRA created