From 14361c39de3f73651c0b8b265f09c3a888b0f12a Mon Sep 17 00:00:00 2001 From: Kristof Csillag Date: Sun, 31 Mar 2024 21:05:52 +0200 Subject: [PATCH] Improve how the pagination engine interfaces with other components W.I.P. --- src/app/components/Table/PaginationEngine.ts | 47 ++++++++++++-- .../Table/useClientSidePagination.ts | 63 +++++++++++++------ .../useComprehensiveSearchParamsPagination.ts | 9 ++- 3 files changed, 91 insertions(+), 28 deletions(-) diff --git a/src/app/components/Table/PaginationEngine.ts b/src/app/components/Table/PaginationEngine.ts index c2787b33b..1ae3febae 100644 --- a/src/app/components/Table/PaginationEngine.ts +++ b/src/app/components/Table/PaginationEngine.ts @@ -7,15 +7,52 @@ export interface SimplePaginationEngine { linkToPage: (pageNumber: number) => To } +/** + * The data returned by a comprehensive pagination engine to the data consumer component + */ export interface PaginatedResults { + /** + * Control interface that can be plugged to a Table's `pagination` prop + */ tablePaginationProps: TablePaginationProps + + /** + * The data provided to the data consumer in the current window + */ data: Item[] | undefined + + /** + * Is the data set still loading from the server? + */ + isLoading: boolean } +/** + * A Comprehensive PaginationEngine sits between the server and the consumer of the data and does transformations + * + * Specifically, the interface for loading the data and the one for the data consumers are decoupled. + */ export interface ComprehensivePaginationEngine { - selectedPage: number - offsetForQuery: number - limitForQuery: number - paramsForQuery: { offset: number; limit: number } - getResults: (queryResult: QueryResult | undefined, key?: keyof QueryResult) => PaginatedResults + /** + * The currently selected page from the data consumer's POV + */ + selectedPageForClient: number + + /** + * Parameters for data to be loaded from the server + */ + paramsForServer: { offset: number; limit: number } + + /** + * Get the current data/state info for the data consumer component. + * + * @param isLoading Is the data still being loaded from the server? + * @param queryResult the data coming in the server, requested according to this engine's specs, including metadata + * @param key The field where the actual records can be found within queryResults + */ + getResults: ( + isLoading: boolean, + queryResult: QueryResult | undefined, + key?: keyof QueryResult, + ) => PaginatedResults } diff --git a/src/app/components/Table/useClientSidePagination.ts b/src/app/components/Table/useClientSidePagination.ts index 8337a8293..beee5dd7b 100644 --- a/src/app/components/Table/useClientSidePagination.ts +++ b/src/app/components/Table/useClientSidePagination.ts @@ -5,10 +5,30 @@ import { List } from '../../../oasis-nexus/api' import { TablePaginationProps } from './TablePagination' type ClientSizePaginationParams = { + /** + * How should we call the query parameter (in the URL)? + */ paramName: string + + /** + * The pagination page size from the POV of the data consumer component + */ clientPageSize: number + + /** + * The pagination page size used for actually loading the data from the server. + * + * Please note that currently this engine doesn't handle when the data consumer requires data which is not + * part of the initial window on the server side. + */ serverPageSize: number - filter?: (item: Item) => boolean + + /** + * Transformation to be applied after loading the data from the server, before presenting it to the data consumer component + * + * Can be used for filtering, ordering, etc + */ + transform?: (input: Item[]) => Item[] } const knownListKeys: string[] = ['total_count', 'is_total_count_clipped'] @@ -27,13 +47,15 @@ function findListIn(data: T): Item[] { } } +/** + * The ClientSidePagination engine loads the data from the server with a big window in one go, for in-memory pagination + */ export function useClientSidePagination({ paramName, clientPageSize, serverPageSize, - filter, + transform, }: ClientSizePaginationParams): ComprehensivePaginationEngine { - const selectedServerPage = 1 const [searchParams] = useSearchParams() const selectedClientPageString = searchParams.get(paramName) const selectedClientPage = parseInt(selectedClientPageString ?? '1', 10) @@ -57,30 +79,35 @@ export function useClientSidePagination({ return { search: newSearchParams.toString() } } - const limit = serverPageSize - const offset = (selectedServerPage - 1) * clientPageSize + // From the server, we always want to load the first batch of data, with the provided (big) window. + // In theory, we could move this window as required, but currently this is not implemented. + const selectedServerPage = 1 + + // The query parameters that should be used for loading the data from the server const paramsForQuery = { - offset, - limit, + offset: (selectedServerPage - 1) * serverPageSize, + limit: serverPageSize, } return { - selectedPage: selectedClientPage, - offsetForQuery: offset, - limitForQuery: limit, - paramsForQuery, - getResults: (queryResult, key) => { - const data = queryResult - ? key - ? (queryResult[key] as Item[]) - : findListIn(queryResult) + selectedPageForClient: selectedClientPage, + paramsForServer: paramsForQuery, + getResults: (isLoading, queryResult, key) => { + const data = queryResult // we want to get list of items out from the incoming results + ? key // do we know where (in which field) to look? + ? (queryResult[key] as Item[]) // If yes, just get out the data + : findListIn(queryResult) // If no, we will try to guess : undefined - const filteredData = !!data && !!filter ? data.filter(filter) : data + // Apply the specified client-side transformation + const filteredData = !!data && !!transform ? transform(data) : data + + // The data window from the POV of the data consumer component const offset = (selectedClientPage - 1) * clientPageSize const limit = clientPageSize const dataWindow = filteredData ? filteredData.slice(offset, offset + limit) : undefined + // The control interface for the data consumer component (i.e. Table) const tableProps: TablePaginationProps = { selectedPage: selectedClientPage, linkToPage, @@ -96,8 +123,8 @@ export function useClientSidePagination({ return { tablePaginationProps: tableProps, data: dataWindow, + isLoading, } }, - // tableProps, } } diff --git a/src/app/components/Table/useComprehensiveSearchParamsPagination.ts b/src/app/components/Table/useComprehensiveSearchParamsPagination.ts index 1ad4006a4..d2e384d00 100644 --- a/src/app/components/Table/useComprehensiveSearchParamsPagination.ts +++ b/src/app/components/Table/useComprehensiveSearchParamsPagination.ts @@ -64,11 +64,9 @@ export function useComprehensiveSearchParamsPagination { + selectedPageForClient: selectedPage, + paramsForServer: paramsForQuery, + getResults: (isLoading, queryResult, key) => { const data = queryResult ? key ? (queryResult[key] as Item[]) @@ -85,6 +83,7 @@ export function useComprehensiveSearchParamsPagination