grafana/ui: Update pagination component for large number of pages (#30151)

This commit is contained in:
Nathan Rodman 2021-02-03 01:00:13 -08:00 committed by GitHub
parent 4ed283e7bf
commit 9b4d4915b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,6 +2,9 @@ import React from 'react';
import { css } from 'emotion';
import { stylesFactory } from '../../themes';
import { Button, ButtonVariant } from '../Button';
import { Icon } from '../Icon/Icon';
const PAGE_LENGTH_TO_CONDENSE = 8;
interface Props {
/** The current page index being shown. */
@ -16,21 +19,84 @@ export const Pagination: React.FC<Props> = ({ currentPage, numberOfPages, onNavi
const styles = getStyles();
const pages = [...new Array(numberOfPages).keys()];
const condensePages = numberOfPages > PAGE_LENGTH_TO_CONDENSE;
const getListItem = (page: number, variant: 'primary' | 'secondary') => (
<li key={page} className={styles.item}>
<Button size="sm" variant={variant} onClick={() => onNavigate(page)}>
{page}
</Button>
</li>
);
const pageButtons = pages.reduce<JSX.Element[]>((pagesToRender, pageIndex) => {
const page = pageIndex + 1;
const variant: ButtonVariant = page === currentPage ? 'primary' : 'secondary';
// The indexes at which to start and stop condensing pages
const lowerBoundIndex = PAGE_LENGTH_TO_CONDENSE;
const upperBoundIndex = numberOfPages - PAGE_LENGTH_TO_CONDENSE + 1;
// When the indexes overlap one another this number is negative
const differenceOfBounds = upperBoundIndex - lowerBoundIndex;
const isFirstOrLastPage = page === 1 || page === numberOfPages;
// This handles when the lowerBoundIndex < currentPage < upperBoundIndex
const currentPageIsBetweenBounds =
differenceOfBounds > -1 && currentPage >= lowerBoundIndex && currentPage <= upperBoundIndex;
if (condensePages) {
if (
isFirstOrLastPage ||
(currentPage < lowerBoundIndex && page < lowerBoundIndex) ||
(differenceOfBounds >= 0 && currentPage > upperBoundIndex && page > upperBoundIndex) ||
(differenceOfBounds < 0 && currentPage >= lowerBoundIndex && page > upperBoundIndex) ||
(currentPageIsBetweenBounds && page >= currentPage - 2 && page <= currentPage + 2)
) {
// Renders a button for the page
pagesToRender.push(getListItem(page, variant));
} else if (
(page === lowerBoundIndex && currentPage < lowerBoundIndex) ||
(page === upperBoundIndex && currentPage > upperBoundIndex) ||
(currentPageIsBetweenBounds && (page === currentPage - 3 || page === currentPage + 3))
) {
// Renders and ellipsis to represent condensed pages
pagesToRender.push(
<li key={page} className={styles.item}>
<Icon className={styles.ellipsis} name="ellipsis-v" />
</li>
);
}
} else {
pagesToRender.push(getListItem(page, variant));
}
return pagesToRender;
}, []);
return (
<div className={styles.container}>
<ol>
{pages.map((pageIndex) => {
const page = pageIndex + 1;
const variant: ButtonVariant = page === currentPage ? 'primary' : 'secondary';
return (
<li key={page} className={styles.item}>
<Button size="sm" variant={variant} onClick={() => onNavigate(page)}>
{page}
</Button>
</li>
);
})}
<li className={styles.item}>
<Button
aria-label="previous"
size="sm"
variant="secondary"
onClick={() => onNavigate(currentPage - 1)}
disabled={currentPage === 1}
>
<Icon name="angle-left" />
</Button>
</li>
{pageButtons}
<li className={styles.item}>
<Button
aria-label="next"
size="sm"
variant="secondary"
onClick={() => onNavigate(currentPage + 1)}
disabled={currentPage === numberOfPages}
>
<Icon name="angle-right" />
</Button>
</li>
</ol>
</div>
);
@ -46,5 +112,8 @@ const getStyles = stylesFactory(() => {
padding-left: 10px;
margin-bottom: 5px;
`,
ellipsis: css`
transform: rotate(90deg);
`,
};
});