Skip to content
This repository was archived by the owner on Jan 16, 2025. It is now read-only.

Add server side pagination #1673

Merged
merged 2 commits into from
Jan 2, 2023
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
76 changes: 48 additions & 28 deletions src/components/Table/TableBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ const BaseTableWrapper = styled.div`
max-width: 100%;
border-bottom: 1px solid ${(props) => props.theme.colors.quartenary.main};
`;
export const DEFAULT_ITEMS_PER_PAGE = 50;
export type Pagination = { itemsPerPage?: number } & (
| { serverSide?: undefined }
| {
serverSide: true;
currentPage: number;
totalItems: number;
}
);

interface InternalTableBaseProps {
hasCheckbox: boolean;
Expand Down Expand Up @@ -170,7 +179,8 @@ export class TableBase<T extends {}> extends React.Component<
}

public componentDidUpdate(prevProps: TableBaseProps<T>) {
const { sort, checkedItems, data, itemsPerPage, rowKey } = this.props;
const { sort, checkedItems, data, pagination, rowKey } = this.props;
const serverSide = pagination?.serverSide;
if (sort && !isEqual(prevProps.sort, sort)) {
this.setState({
sort,
Expand All @@ -186,10 +196,12 @@ export class TableBase<T extends {}> extends React.Component<
this.setRowSelection(checkedItems);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need to doublecheck how selection works.

  • We need to update toggleChecked, since it does include a client side sorting and assumes it has all the data while trying to map the checkbox the user clicked and the item that the row was about.
  • We probably also need to check toggleAllChecked as well, and maybe in server side more it should be returning all, to let the consumer know that they need to query and fetch AAAAAALLLLLLLLLL item IDs from the backend.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Work in progress. I need to merge this PR first to be able to do something like this in the following PR:

isDisabled & actionFn:

  • isServerSide: false => T[]
  • isServerSide: true =>
  • selecting N items: { id: { $in: [] } }
  • select-all: { $and: [{ belongs_to__application: xyz }, searchFilters] }

}

const totalItems = data?.length ?? 0;
const totalItems = serverSide ? pagination?.totalItems : data?.length ?? 0;
const currentPage = serverSide ? pagination?.currentPage : this.state.page;
const itemsPerPage = this.props.itemsPerPage ?? pagination?.itemsPerPage;
if (
this.state.page !== 0 &&
totalItems <= this.state.page * (itemsPerPage ?? 50)
currentPage !== 0 &&
totalItems <= currentPage * (itemsPerPage ?? DEFAULT_ITEMS_PER_PAGE)
) {
this.resetPager();
}
Expand Down Expand Up @@ -462,19 +474,20 @@ export class TableBase<T extends {}> extends React.Component<
};

public toggleSort = (e: React.MouseEvent<HTMLButtonElement>) => {
const { field } = e.currentTarget.dataset;
const { field, refScheme } = e.currentTarget.dataset;
const { sort } = this.state;
if (!field) {
return;
}

let nextSort = {
field: field as keyof T,
refScheme,
reverse: false,
};

if (sort.field === field) {
nextSort = { field: sort.field, reverse: !sort.reverse };
nextSort = { field: sort.field, refScheme, reverse: !sort.reverse };
}

this.setState({ sort: nextSort });
Expand Down Expand Up @@ -522,11 +535,10 @@ export class TableBase<T extends {}> extends React.Component<
}
};

public setPage = (change: any) => {
if (this.props.onPageChange) {
this.props.onPageChange(change);
}

public setPage = (change: number) => {
const { pagination } = this.props;
const itemsPerPage = this.props.itemsPerPage ?? pagination?.itemsPerPage;
this.props.onPageChange?.(change, itemsPerPage ?? DEFAULT_ITEMS_PER_PAGE);
this.setState({ page: change });
};

Expand All @@ -547,7 +559,6 @@ export class TableBase<T extends {}> extends React.Component<
columns,
data,
usePager,
itemsPerPage,
pagerPosition,
rowAnchorAttributes,
rowKey,
Expand All @@ -557,36 +568,40 @@ export class TableBase<T extends {}> extends React.Component<
getRowClass,
className,
fuzzyPager,
pagination,
} = this.props;

const { page, sort } = this.state;
const serverSide = pagination?.serverSide;
const items = data || [];
const totalItems = items.length;
const _itemsPerPage = itemsPerPage || 50;
const totalItems = serverSide ? pagination?.totalItems : items.length;
const itemsPerPage =
this.props.itemsPerPage ??
pagination?.itemsPerPage ??
DEFAULT_ITEMS_PER_PAGE;
const _pagerPosition = pagerPosition || 'top';
let sortedData = items;

const lowerBound = usePager ? page * _itemsPerPage : 0;
const upperBound = usePager
? Math.min((page + 1) * _itemsPerPage, totalItems)
: totalItems;

const sortedData = this.sortData(items).slice(lowerBound, upperBound);

const shouldShowPaper = !!usePager && totalItems > 0;
if (!serverSide) {
const lowerBound = page * itemsPerPage;
const upperBound = Math.min((page + 1) * itemsPerPage, items.length);
sortedData = this.sortData(items).slice(lowerBound, upperBound);
}

const shouldShowPager = !!usePager && totalItems > 0;
const checkedRowIdentifiers = this.getCheckedRowIdentifiers();
const highlightedRowIdentifiers = this.getHighlightedRowIdentifiers();
const disabledRowIdentifiers = this.getDisabledRowIdentifiers();

return (
<>
{shouldShowPaper &&
{shouldShowPager &&
(_pagerPosition === 'top' || _pagerPosition === 'both') && (
<Pager
fuzzy={fuzzyPager}
totalItems={totalItems}
itemsPerPage={_itemsPerPage}
page={page}
itemsPerPage={itemsPerPage}
page={serverSide ? pagination?.currentPage : page}
nextPage={this.incrementPage}
prevPage={this.decrementPage}
mb={2}
Expand Down Expand Up @@ -620,6 +635,7 @@ export class TableBase<T extends {}> extends React.Component<
>
<Button
data-field={item.field}
data-ref-scheme={item.refScheme}
plain
primary={sort.field === item.field}
onClick={this.toggleSort}
Expand Down Expand Up @@ -692,12 +708,12 @@ export class TableBase<T extends {}> extends React.Component<
</Base>
</BaseTableWrapper>

{shouldShowPaper &&
{shouldShowPager &&
(_pagerPosition === 'bottom' || _pagerPosition === 'both') && (
<Pager
fuzzy={fuzzyPager}
totalItems={totalItems}
itemsPerPage={_itemsPerPage}
itemsPerPage={itemsPerPage}
page={page}
nextPage={this.incrementPage}
prevPage={this.decrementPage}
Expand All @@ -712,6 +728,7 @@ export class TableBase<T extends {}> extends React.Component<
export interface TableSortOptions<T> {
reverse: boolean;
field: keyof T | null;
refScheme?: string;
}

export interface TableBaseProps<T> {
Expand All @@ -730,7 +747,7 @@ export interface TableBaseProps<T> {
/** A function that is called when a column is sorted */
onSort?: (sort: TableSortOptions<T>) => void;
/** A function that is called when the page is incremented, decremented and reset */
onPageChange?: (page: number) => void;
onPageChange?: (page: number, itemsPerPage: number) => void;
/** sort options to be used both as a default sort, and on subsequent renders if the passed sort changes */
sort?: TableSortOptions<T>;
/** Attributes to pass to the anchor element used in a row */
Expand All @@ -752,7 +769,10 @@ export interface TableBaseProps<T> {
/** If true, the total number of items shown on the page will be indicated as being approximate. Useful for when you don't now the full size of your dataset. Only used if `usePager` is true. */
fuzzyPager?: boolean;
/** The number of items to be shown per page. Only used if `usePager` is true. Defaults to 50. */
/** @deprecated use pagination.itemsPerPage */
itemsPerPage?: number;
/** Information from a server side pagination */
pagination?: Pagination;
/** Sets whether the pager is displayed at the top of the table, the bottom of the table or in both positions. Only used if `usePager` is true. Defaults to `top`. */
pagerPosition?: 'top' | 'bottom' | 'both';
className?: string;
Expand Down
1 change: 1 addition & 0 deletions src/components/Table/TableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface TableBaseColumn<T> {
row: T,
) => string | number | JSX.Element | null | undefined;
sortable?: boolean | TableSortFunction<T>;
refScheme?: string;
}

export interface TableRowProps<T> {
Expand Down
16 changes: 13 additions & 3 deletions src/components/Table/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import * as React from 'react';
import { TableBase, TableBaseProps, TableSortOptions } from './TableBase';
import {
TableBase,
TableBaseProps,
TableSortOptions,
Pagination,
} from './TableBase';
import styled, { css } from 'styled-components';
import keys from 'lodash/keys';
import pick from 'lodash/pick';
Expand Down Expand Up @@ -710,7 +715,6 @@ export class Table<T extends {}> extends React.Component<
onRowClick,
...props
} = this.props;

const sort =
sortProp || (sortingStateRestorationKey ? this.state.sort : undefined);

Expand Down Expand Up @@ -741,4 +745,10 @@ export class Table<T extends {}> extends React.Component<
}
}

export { TableBaseColumn, TableRow, TableSortOptions, TableSortFunction };
export {
TableBaseColumn,
TableRow,
TableSortOptions,
TableSortFunction,
Pagination,
};
4 changes: 2 additions & 2 deletions src/components/Table/spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ describe('Table component', () => {
}
data={PokeDex}
usePager
itemsPerPage={3}
pagination={{ itemsPerPage: 3 }}
/>
</Provider>,
);
Expand Down Expand Up @@ -950,7 +950,7 @@ describe('Table component', () => {
{
columns: [{ field: 'Name' }] as any,
data: PokeDex,
itemsPerPage: 2,
pagination: { itemsPerPage: 2 },
usePager: true,
},
),
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useTranslation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const translationMap = {

'warning.etcher_min_requirement': 'Etcher v1.7.2 or greater is required',

'loading.generic': 'Loading...',
'loading.generating_configuration_file': 'Generating configuration file...',
'loading.fetching_versions': 'Fetching versions...',

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export {
TableSortOptions,
TableProps,
TableSortFunction,
Pagination,
} from './components/Table';

export { Accordion, AccordionProps } from './components/Accordion';
Expand Down