fix(ui/datatables): sync page count with filtering [EE-5890] (#10010)

pull/10090/head
Chaim Lev-Ari 2023-08-22 09:36:31 +03:00 committed by GitHub
parent bb646162d1
commit 5586910e9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 78 additions and 63 deletions

View File

@ -8,29 +8,22 @@ interface Props {
boundaryLinks?: boolean; boundaryLinks?: boolean;
currentPage: number; currentPage: number;
directionLinks?: boolean; directionLinks?: boolean;
itemsPerPage: number;
onPageChange(page: number): void; onPageChange(page: number): void;
totalCount: number; pageCount: number;
maxSize: number; maxSize: number;
isInputVisible?: boolean; isInputVisible?: boolean;
} }
export function PageSelector({ export function PageSelector({
currentPage, currentPage,
totalCount, pageCount,
itemsPerPage,
onPageChange, onPageChange,
maxSize = 5, maxSize = 5,
directionLinks = true, directionLinks = true,
boundaryLinks = false, boundaryLinks = false,
isInputVisible = false, isInputVisible = false,
}: Props) { }: Props) {
const pages = generatePagesArray( const pages = generatePagesArray(currentPage, pageCount, maxSize);
currentPage,
totalCount,
itemsPerPage,
maxSize
);
const last = pages[pages.length - 1]; const last = pages[pages.length - 1];
if (pages.length <= 1) { if (pages.length <= 1) {
@ -42,7 +35,7 @@ export function PageSelector({
{isInputVisible && ( {isInputVisible && (
<PageInput <PageInput
onChange={(page) => onPageChange(page)} onChange={(page) => onPageChange(page)}
totalPages={Math.ceil(totalCount / itemsPerPage)} totalPages={pageCount}
/> />
)} )}
<ul className="pagination"> <ul className="pagination">

View File

@ -9,7 +9,7 @@ interface Props {
page: number; page: number;
pageLimit: number; pageLimit: number;
showAll?: boolean; showAll?: boolean;
totalCount: number; pageCount: number;
isPageInputVisible?: boolean; isPageInputVisible?: boolean;
className?: string; className?: string;
} }
@ -20,7 +20,7 @@ export function PaginationControls({
onPageLimitChange, onPageLimitChange,
showAll, showAll,
onPageChange, onPageChange,
totalCount, pageCount,
isPageInputVisible, isPageInputVisible,
className, className,
}: Props) { }: Props) {
@ -38,8 +38,7 @@ export function PaginationControls({
maxSize={5} maxSize={5}
onPageChange={onPageChange} onPageChange={onPageChange}
currentPage={page} currentPage={page}
itemsPerPage={pageLimit} pageCount={pageCount}
totalCount={totalCount}
isInputVisible={isPageInputVisible} isInputVisible={isPageInputVisible}
/> />
)} )}

View File

@ -12,12 +12,10 @@ export /**
*/ */
function generatePagesArray( function generatePagesArray(
currentPage: number, currentPage: number,
collectionLength: number, totalPages: number,
rowsPerPage: number,
paginationRange: number paginationRange: number
): (number | '...')[] { ): (number | '...')[] {
const pages: (number | '...')[] = []; const pages: (number | '...')[] = [];
const totalPages = Math.ceil(collectionLength / rowsPerPage);
const halfWay = Math.ceil(paginationRange / 2); const halfWay = Math.ceil(paginationRange / 2);
let position; let position;

View File

@ -34,6 +34,20 @@ import { createSelectColumn } from './select-column';
import { TableRow } from './TableRow'; import { TableRow } from './TableRow';
import { type TableState as GlobalTableState } from './useTableState'; import { type TableState as GlobalTableState } from './useTableState';
export type PaginationProps =
| {
isServerSidePagination?: false;
totalCount?: never;
page?: never;
onPageChange?: never;
}
| {
isServerSidePagination: true;
totalCount: number;
page: number;
onPageChange(page: number): void;
};
export interface Props< export interface Props<
D extends Record<string, unknown>, D extends Record<string, unknown>,
TMeta extends TableMeta<D> = TableMeta<D> TMeta extends TableMeta<D> = TableMeta<D>
@ -50,13 +64,8 @@ export interface Props<
titleIcon?: IconProps['icon']; titleIcon?: IconProps['icon'];
initialTableState?: Partial<TableState>; initialTableState?: Partial<TableState>;
isLoading?: boolean; isLoading?: boolean;
totalCount?: number;
description?: ReactNode; description?: ReactNode;
pageCount?: number;
highlightedItemId?: string; highlightedItemId?: string;
page?: number;
onPageChange?(page: number): void;
settingsManager: GlobalTableState<BasicTableSettings>; settingsManager: GlobalTableState<BasicTableSettings>;
renderRow?(row: Row<D>, highlightedItemId?: string): ReactNode; renderRow?(row: Row<D>, highlightedItemId?: string): ReactNode;
getRowCanExpand?(row: Row<D>): boolean; getRowCanExpand?(row: Row<D>): boolean;
@ -80,11 +89,7 @@ export function Datatable<
emptyContentLabel, emptyContentLabel,
initialTableState = {}, initialTableState = {},
isLoading, isLoading,
totalCount = dataset.length,
description, description,
pageCount,
page,
onPageChange = () => null,
settingsManager: settings, settingsManager: settings,
renderRow = defaultRenderRow, renderRow = defaultRenderRow,
highlightedItemId, highlightedItemId,
@ -92,8 +97,16 @@ export function Datatable<
getRowCanExpand, getRowCanExpand,
'data-cy': dataCy, 'data-cy': dataCy,
meta, meta,
}: Props<D, TMeta>) { onPageChange = () => {},
const isServerSidePagination = typeof pageCount !== 'undefined'; page,
totalCount = dataset.length,
isServerSidePagination = false,
}: Props<D, TMeta> & PaginationProps) {
const pageCount = useMemo(
() => Math.ceil(totalCount / settings.pageSize),
[settings.pageSize, totalCount]
);
const enableRowSelection = getIsSelectionEnabled( const enableRowSelection = getIsSelectionEnabled(
disableSelect, disableSelect,
isRowSelectable isRowSelectable
@ -120,6 +133,7 @@ export function Datatable<
defaultColumn: { defaultColumn: {
enableColumnFilter: false, enableColumnFilter: false,
enableHiding: true, enableHiding: true,
sortingFn: 'alphanumeric',
}, },
enableRowSelection, enableRowSelection,
autoResetExpanded: false, autoResetExpanded: false,
@ -127,7 +141,6 @@ export function Datatable<
getRowId, getRowId,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(), getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(), getPaginationRowModel: getPaginationRowModel(),
getFacetedRowModel: getFacetedRowModel(), getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(), getFacetedUniqueValues: getFacetedUniqueValues(),
@ -135,7 +148,11 @@ export function Datatable<
getExpandedRowModel: getExpandedRowModel(), getExpandedRowModel: getExpandedRowModel(),
getRowCanExpand, getRowCanExpand,
getColumnCanGlobalFilter, getColumnCanGlobalFilter,
...(isServerSidePagination ? { manualPagination: true, pageCount } : {}), ...(isServerSidePagination
? { manualPagination: true, pageCount }
: {
getSortedRowModel: getSortedRowModel(),
}),
meta, meta,
}); });
@ -178,7 +195,7 @@ export function Datatable<
onPageSizeChange={handlePageSizeChange} onPageSizeChange={handlePageSizeChange}
page={typeof page === 'number' ? page : tableState.pagination.pageIndex} page={typeof page === 'number' ? page : tableState.pagination.pageIndex}
pageSize={tableState.pagination.pageSize} pageSize={tableState.pagination.pageSize}
totalCount={totalCount} pageCount={tableInstance.getPageCount()}
totalSelected={selectedItems.length} totalSelected={selectedItems.length}
/> />
</Table.Container> </Table.Container>

View File

@ -8,7 +8,7 @@ interface Props {
pageSize: number; pageSize: number;
page: number; page: number;
onPageChange(page: number): void; onPageChange(page: number): void;
totalCount: number; pageCount: number;
onPageSizeChange(pageSize: number): void; onPageSizeChange(pageSize: number): void;
} }
@ -17,7 +17,7 @@ export function DatatableFooter({
pageSize, pageSize,
page, page,
onPageChange, onPageChange,
totalCount, pageCount,
onPageSizeChange, onPageSizeChange,
}: Props) { }: Props) {
return ( return (
@ -28,7 +28,7 @@ export function DatatableFooter({
pageLimit={pageSize} pageLimit={pageSize}
page={page + 1} page={page + 1}
onPageChange={(page) => onPageChange(page - 1)} onPageChange={(page) => onPageChange(page - 1)}
totalCount={totalCount} pageCount={pageCount}
onPageLimitChange={onPageSizeChange} onPageLimitChange={onPageSizeChange}
/> />
</Table.Footer> </Table.Footer>

View File

@ -2,7 +2,11 @@ import { Row } from '@tanstack/react-table';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { ExpandableDatatableTableRow } from './ExpandableDatatableRow'; import { ExpandableDatatableTableRow } from './ExpandableDatatableRow';
import { Datatable, Props as DatatableProps } from './Datatable'; import {
Datatable,
Props as DatatableProps,
PaginationProps,
} from './Datatable';
interface Props<D extends Record<string, unknown>> interface Props<D extends Record<string, unknown>>
extends Omit<DatatableProps<D>, 'renderRow' | 'expandable'> { extends Omit<DatatableProps<D>, 'renderRow' | 'expandable'> {
@ -15,7 +19,7 @@ export function ExpandableDatatable<D extends Record<string, unknown>>({
getRowCanExpand = () => true, getRowCanExpand = () => true,
expandOnRowClick, expandOnRowClick,
...props ...props
}: Props<D>) { }: Props<D> & PaginationProps) {
return ( return (
<Datatable<D> <Datatable<D>
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading

View File

@ -17,7 +17,6 @@ export function buildNameColumn<T extends Record<string, unknown>>(
cell, cell,
enableSorting: true, enableSorting: true,
enableHiding: false, enableHiding: false,
sortingFn: 'text',
}; };
function createCell<T extends Record<string, unknown>>() { function createCell<T extends Record<string, unknown>>() {

View File

@ -51,10 +51,10 @@ export function EdgeGroupAssociationTable({
onClickRow: (env: Environment) => void; onClickRow: (env: Environment) => void;
} & AutomationTestingProps) { } & AutomationTestingProps) {
const tableState = useTableStateWithoutStorage('Name'); const tableState = useTableStateWithoutStorage('Name');
const [page, setPage] = useState(1); const [page, setPage] = useState(0);
const environmentsQuery = useEnvironmentList({ const environmentsQuery = useEnvironmentList({
pageLimit: tableState.pageSize, pageLimit: tableState.pageSize,
page, page: page + 1,
search: tableState.search, search: tableState.search,
sort: tableState.sortBy.id as 'Group' | 'Name', sort: tableState.sortBy.id as 'Group' | 'Name',
order: tableState.sortBy.desc ? 'desc' : 'asc', order: tableState.sortBy.desc ? 'desc' : 'asc',
@ -87,8 +87,10 @@ export function EdgeGroupAssociationTable({
columns={columns} columns={columns}
settingsManager={tableState} settingsManager={tableState}
dataset={environments} dataset={environments}
isServerSidePagination
page={page}
onPageChange={setPage} onPageChange={setPage}
pageCount={Math.ceil(totalCount / tableState.pageSize)} totalCount={totalCount}
renderRow={(row) => ( renderRow={(row) => (
<TableRow<DecoratedEnvironment> <TableRow<DecoratedEnvironment>
cells={row.getVisibleCells()} cells={row.getVisibleCells()}
@ -98,7 +100,6 @@ export function EdgeGroupAssociationTable({
emptyContentLabel={emptyContentLabel} emptyContentLabel={emptyContentLabel}
data-cy={dataCy} data-cy={dataCy}
disableSelect disableSelect
totalCount={totalCount}
/> />
); );
} }

View File

@ -24,8 +24,6 @@ export function Datatable() {
search: tableState.search, search: tableState.search,
}); });
const pageCount = Math.ceil(totalCount / tableState.pageSize);
return ( return (
<GenericDatatable <GenericDatatable
settingsManager={tableState} settingsManager={tableState}
@ -37,10 +35,10 @@ export function Datatable() {
<TableActions selectedRows={selectedRows} /> <TableActions selectedRows={selectedRows} />
)} )}
isLoading={isLoading} isLoading={isLoading}
totalCount={totalCount} isServerSidePagination
pageCount={pageCount}
page={page} page={page}
onPageChange={setPage} onPageChange={setPage}
totalCount={totalCount}
description={<Filter />} description={<Filter />}
/> />
); );

View File

@ -40,7 +40,7 @@ export function EnvironmentsDatatable() {
(value) => (value ? parseInt(value, 10) : undefined) (value) => (value ? parseInt(value, 10) : undefined)
); );
const tableState = useTableStateWithoutStorage('name'); const tableState = useTableStateWithoutStorage('name');
const endpointsQuery = useEnvironmentList({ const environmentsQuery = useEnvironmentList({
pageLimit: tableState.pageSize, pageLimit: tableState.pageSize,
page: page + 1, page: page + 1,
search: tableState.search, search: tableState.search,
@ -56,7 +56,7 @@ export function EnvironmentsDatatable() {
const gitConfigCommitHash = edgeStackQuery.data?.GitConfig?.ConfigHash || ''; const gitConfigCommitHash = edgeStackQuery.data?.GitConfig?.ConfigHash || '';
const environments: Array<EdgeStackEnvironment> = useMemo( const environments: Array<EdgeStackEnvironment> = useMemo(
() => () =>
endpointsQuery.environments.map( environmentsQuery.environments.map(
(env) => (env) =>
({ ({
...env, ...env,
@ -72,7 +72,7 @@ export function EnvironmentsDatatable() {
[ [
currentFileVersion, currentFileVersion,
edgeStackQuery.data?.Status, edgeStackQuery.data?.Status,
endpointsQuery.environments, environmentsQuery.environments,
gitConfigCommitHash, gitConfigCommitHash,
gitConfigURL, gitConfigURL,
] ]
@ -81,13 +81,15 @@ export function EnvironmentsDatatable() {
return ( return (
<Datatable <Datatable
columns={columns} columns={columns}
isLoading={endpointsQuery.isLoading} isLoading={environmentsQuery.isLoading}
dataset={environments} dataset={environments}
settingsManager={tableState} settingsManager={tableState}
title="Environments Status" title="Environments Status"
titleIcon={HardDrive} titleIcon={HardDrive}
isServerSidePagination
page={page}
onPageChange={setPage} onPageChange={setPage}
totalCount={endpointsQuery.totalCount} totalCount={environmentsQuery.totalCount}
emptyContentLabel="No environment available." emptyContentLabel="No environment available."
disableSelect disableSelect
description={ description={

View File

@ -28,7 +28,6 @@ export function EventsDatatable({ data, isLoading }: EventsDatatableProps) {
dataset={data} dataset={data}
titleIcon={History} titleIcon={History}
title="Events" title="Events"
totalCount={data.length}
getRowId={(row) => `${row.Date}-${row.Message}-${row.Type}`} getRowId={(row) => `${row.Date}-${row.Message}-${row.Type}`}
disableSelect disableSelect
/> />

View File

@ -83,7 +83,7 @@ export function AssociateAMTDialog({
onPageChange={setPage} onPageChange={setPage}
pageLimit={pageLimit} pageLimit={pageLimit}
onPageLimitChange={setPageLimit} onPageLimitChange={setPageLimit}
totalCount={totalCount} pageCount={Math.ceil(totalCount / pageLimit)}
/> />
</div> </div>
</div> </div>

View File

@ -239,7 +239,7 @@ export function EnvironmentList({ onClickBrowse, onRefresh }: Props) {
pageLimit={pageLimit} pageLimit={pageLimit}
page={page} page={page}
onPageChange={setPage} onPageChange={setPage}
totalCount={totalCount} pageCount={Math.ceil(totalCount / pageLimit)}
onPageLimitChange={setPageLimit} onPageLimitChange={setPageLimit}
/> />
</TableFooter> </TableFooter>

View File

@ -111,7 +111,7 @@ export function KubeconfigPrompt({
onPageChange={setPage} onPageChange={setPage}
pageLimit={pageLimit} pageLimit={pageLimit}
onPageLimitChange={setPageLimit} onPageLimitChange={setPageLimit}
totalCount={totalCount} pageCount={Math.ceil(totalCount / pageLimit)}
/> />
</div> </div>
</div> </div>

View File

@ -12,6 +12,7 @@ import { useTableState } from '@@/datatables/useTableState';
import { isBE } from '../../feature-flags/feature-flags.service'; import { isBE } from '../../feature-flags/feature-flags.service';
import { isSortType } from '../queries/useEnvironmentList'; import { isSortType } from '../queries/useEnvironmentList';
import { EnvironmentStatus } from '../types';
import { columns } from './columns'; import { columns } from './columns';
import { EnvironmentListItem } from './types'; import { EnvironmentListItem } from './types';
@ -60,10 +61,14 @@ export function EnvironmentsDatatable({
dataset={environmentsWithGroups} dataset={environmentsWithGroups}
columns={columns} columns={columns}
settingsManager={tableState} settingsManager={tableState}
pageCount={Math.ceil(totalCount / tableState.pageSize)} isServerSidePagination
page={page}
onPageChange={setPage} onPageChange={setPage}
isLoading={isLoading}
totalCount={totalCount} totalCount={totalCount}
isLoading={isLoading}
isRowSelectable={(row) =>
row.original.Status !== EnvironmentStatus.Provisioning
}
renderTableActions={(selectedRows) => ( renderTableActions={(selectedRows) => (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button <Button

View File

@ -28,6 +28,7 @@ export function AssociatedEnvironmentsSelector({
emptyContentLabel="No environment available" emptyContentLabel="No environment available"
query={{ query={{
groupIds: [1], groupIds: [1],
excludeIds: value,
}} }}
onClickRow={(env) => { onClickRow={(env) => {
if (!value.includes(env.Id)) { if (!value.includes(env.Id)) {

View File

@ -33,10 +33,10 @@ export function GroupAssociationTable({
onClickRow?: (env: Environment) => void; onClickRow?: (env: Environment) => void;
} & AutomationTestingProps) { } & AutomationTestingProps) {
const tableState = useTableStateWithoutStorage('Name'); const tableState = useTableStateWithoutStorage('Name');
const [page, setPage] = useState(1); const [page, setPage] = useState(0);
const environmentsQuery = useEnvironmentList({ const environmentsQuery = useEnvironmentList({
pageLimit: tableState.pageSize, pageLimit: tableState.pageSize,
page, page: page + 1,
search: tableState.search, search: tableState.search,
sort: tableState.sortBy.id as 'Name', sort: tableState.sortBy.id as 'Name',
order: tableState.sortBy.desc ? 'desc' : 'asc', order: tableState.sortBy.desc ? 'desc' : 'asc',
@ -51,8 +51,10 @@ export function GroupAssociationTable({
columns={columns} columns={columns}
settingsManager={tableState} settingsManager={tableState}
dataset={environments} dataset={environments}
isServerSidePagination
page={page}
onPageChange={setPage} onPageChange={setPage}
pageCount={Math.ceil(environmentsQuery.totalCount / tableState.pageSize)} totalCount={environmentsQuery.totalCount}
renderRow={(row) => ( renderRow={(row) => (
<TableRow<Environment> <TableRow<Environment>
cells={row.getVisibleCells()} cells={row.getVisibleCells()}
@ -62,7 +64,6 @@ export function GroupAssociationTable({
emptyContentLabel={emptyContentLabel} emptyContentLabel={emptyContentLabel}
data-cy={dataCy} data-cy={dataCy}
disableSelect disableSelect
totalCount={environmentsQuery.totalCount}
/> />
); );
} }

View File

@ -53,7 +53,6 @@ export function ListView() {
titleIcon={Clock} titleIcon={Clock}
emptyContentLabel="No schedules found" emptyContentLabel="No schedules found"
isLoading={listQuery.isLoading} isLoading={listQuery.isLoading}
totalCount={listQuery.data.length}
renderTableActions={(selectedRows) => ( renderTableActions={(selectedRows) => (
<TableActions selectedRows={selectedRows} /> <TableActions selectedRows={selectedRows} />
)} )}

View File

@ -48,7 +48,6 @@ export function NotificationsView() {
dataset={userNotifications} dataset={userNotifications}
settingsManager={tableState} settingsManager={tableState}
emptyContentLabel="No notifications found" emptyContentLabel="No notifications found"
totalCount={userNotifications.length}
renderTableActions={(selectedRows) => ( renderTableActions={(selectedRows) => (
<TableActions selectedRows={selectedRows} /> <TableActions selectedRows={selectedRows} />
)} )}