Skip to content

Commit

Permalink
feat: add BE pagination to table with pages (#985)
Browse files Browse the repository at this point in the history
  • Loading branch information
simcha90 authored and zhaoyongjie committed Nov 26, 2021
1 parent 15be1fa commit 103f8e4
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 178 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { Registry, makeSingleton, OverwritePolicy, QueryContext } from '../..';
import { Registry, makeSingleton, OverwritePolicy, QueryContext, SetDataMaskHook } from '../..';

// Ideally this would be <T extends QueryFormData>
type BuildQuery = (formData: any) => QueryContext;
export type BuildQuery<T = any> = (
formData: T,
options?: {
extras?: {
cachedChanges?: any;
};
hooks?: {
setDataMask: SetDataMaskHook;
setCachedChanges: (newChanges: any) => void;
};
},
) => QueryContext;

class ChartBuildQueryRegistry extends Registry<BuildQuery> {
constructor() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { QueryFormData, QueryContext } from '../..';
import { QueryFormData, QueryContext, SetDataMaskHook } from '../..';
import ChartProps from '../models/ChartProps';
import { PlainObject } from './Base';

Expand All @@ -10,6 +10,17 @@ export type PreTransformProps = TransformFunction<ChartProps, ChartProps>;
export type TransformProps<Props extends ChartProps = ChartProps> = TransformFunction<Props>;
export type PostTransformProps = TransformFunction;

export type BuildQueryFunction<T extends QueryFormData> = (formData: T) => QueryContext;
export type BuildQueryFunction<T extends QueryFormData> = (
formData: T,
options?: {
extras?: {
cachedChanges?: any;
};
hooks?: {
setDataMask: SetDataMaskHook;
setCachedChanges: (newChanges: any) => void;
};
},
) => QueryContext;

export default {};
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,46 @@ import buildQueryObject from './buildQueryObject';
import DatasourceKey from './DatasourceKey';
import { QueryFieldAliases, QueryFormData } from './types/QueryFormData';
import { QueryContext, QueryObject } from './types/Query';
import { SetDataMaskHook } from '../chart';

const WRAP_IN_ARRAY = (baseQueryObject: QueryObject) => [baseQueryObject];
const WRAP_IN_ARRAY = (
baseQueryObject: QueryObject,
options?: {
extras?: {
cachedChanges?: any;
};
hooks?: {
setDataMask: SetDataMaskHook;
setCachedChanges: (newChanges: any) => void;
};
},
) => [baseQueryObject];

export type BuildFinalQuerieObjects = (baseQueryObject: QueryObject) => QueryObject[];
export type BuildFinalQueryObjects = (baseQueryObject: QueryObject) => QueryObject[];

export default function buildQueryContext(
formData: QueryFormData,
options?:
| {
buildQuery?: BuildFinalQuerieObjects;
buildQuery?: BuildFinalQueryObjects;
queryFields?: QueryFieldAliases;
hooks?: { setDataMask: SetDataMaskHook };
}
| BuildFinalQuerieObjects,
| BuildFinalQueryObjects,
): QueryContext {
const { queryFields, buildQuery = WRAP_IN_ARRAY } =
const { queryFields, buildQuery = WRAP_IN_ARRAY, hooks = {} } =
typeof options === 'function' ? { buildQuery: options, queryFields: {} } : options || {};
return {
datasource: new DatasourceKey(formData.datasource).toObject(),
force: formData.force || false,
queries: buildQuery(buildQueryObject(formData, queryFields)),
queries: buildQuery(buildQueryObject(formData, queryFields), {
extras: {},
hooks: {
setDataMask: () => {},
setCachedChanges: () => {},
...hooks,
},
}),
result_format: formData.result_format || 'json',
result_type: formData.result_type || 'full',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,11 @@ import {
Row,
} from 'react-table';
import { matchSorter, rankings } from 'match-sorter';
import { SetDataMaskHook } from '@superset-ui/core';
import GlobalFilter, { GlobalFilterProps } from './components/GlobalFilter';
import SelectPageSize, { SelectPageSizeProps, SizeOption } from './components/SelectPageSize';
import SimplePagination from './components/Pagination';
import useSticky from './hooks/useSticky';
import { updateExternalFormData } from './utils/externalAPIs';
import ServerPagination from './components/ServerPagination';
import { ServerPage } from '../types';
import { PAGE_SIZE_OPTIONS } from '../consts';

export interface DataTableProps<D extends object> extends TableOptions<D> {
tableClassName?: string;
Expand All @@ -55,12 +52,12 @@ export interface DataTableProps<D extends object> extends TableOptions<D> {
width?: string | number;
height?: string | number;
serverPagination?: boolean;
setDataMask: SetDataMaskHook;
currentPage?: number;
onServerPaginationChange: (pageNumber: number, pageSize: number) => void;
serverPaginationData: { pageSize?: number; currentPage?: number };
pageSize?: number;
noResults?: string | ((filterString: string) => ReactNode);
sticky?: boolean;
showNextButton: boolean;
rowCount: number;
wrapperRef?: MutableRefObject<HTMLDivElement>;
}

Expand All @@ -73,17 +70,17 @@ export default function DataTable<D extends object>({
tableClassName,
columns,
data,
currentPage = 0,
serverPaginationData,
width: initialWidth = '100%',
height: initialHeight = 300,
pageSize: initialPageSize = 0,
initialState: initialState_ = {},
pageSizeOptions = [10, 25, 50, 100, 200],
pageSizeOptions = PAGE_SIZE_OPTIONS,
maxPageItemCount = 9,
sticky: doSticky,
searchInput = true,
setDataMask,
showNextButton,
onServerPaginationChange,
rowCount,
selectPageSize,
noResults: noResultsText = 'No data found',
hooks,
Expand All @@ -98,7 +95,7 @@ export default function DataTable<D extends object>({
doSticky ? useSticky : [],
hooks || [],
].flat();
const resultsSize = data.length;
const resultsSize = serverPagination ? rowCount : data.length;
const sortByRef = useRef([]); // cache initial `sortby` so sorting doesn't trigger page reset
const pageSizeRef = useRef([initialPageSize, resultsSize]);
const hasPagination = initialPageSize > 0 && resultsSize > 0; // pageSize == 0 means no pagination
Expand Down Expand Up @@ -129,7 +126,16 @@ export default function DataTable<D extends object>({
}
return undefined;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialHeight, initialWidth, wrapperRef, hasPagination, hasGlobalControl, showNextButton]);
}, [
initialHeight,
initialWidth,
wrapperRef,
hasPagination,
hasGlobalControl,
paginationRef,
resultsSize,
JSON.stringify(serverPaginationData),
]);

const defaultGlobalFilter: FilterType<D> = useCallback(
(rows: Row<D>[], columnIds: IdType<D>[], filterValue: string) => {
Expand Down Expand Up @@ -170,7 +176,7 @@ export default function DataTable<D extends object>({
// make setPageSize accept 0
const setPageSize = (size: number) => {
if (serverPagination) {
updateExternalFormData(setDataMask, 0, size);
onServerPaginationChange(0, size);
}
// keep the original size if data is empty
if (size || resultsSize !== 0) {
Expand Down Expand Up @@ -224,7 +230,7 @@ export default function DataTable<D extends object>({
</table>
);

// force upate the pageSize when it's been update from the initial state
// force update the pageSize when it's been update from the initial state
if (
pageSizeRef.current[0] !== initialPageSize ||
// when initialPageSize stays as zero, but total number of records changed,
Expand All @@ -235,16 +241,29 @@ export default function DataTable<D extends object>({
setPageSize(initialPageSize);
}

const goToBEPage = (direction: ServerPage) => {
updateExternalFormData(
setDataMask,
direction === ServerPage.NEXT ? currentPage + 1 : currentPage - 1,
pageSize,
);
};

const paginationStyle: CSSProperties = sticky.height ? {} : { visibility: 'hidden' };

let resultPageCount = pageCount;
let resultCurrentPageSize = pageSize;
let resultCurrentPage = pageIndex;
let resultOnPageChange: (page: number) => void = gotoPage;
if (serverPagination) {
const serverPageSize = serverPaginationData.pageSize ?? initialPageSize;
resultPageCount = Math.ceil(rowCount / serverPageSize);
if (!Number.isFinite(resultPageCount)) {
resultPageCount = 0;
}
resultCurrentPageSize = serverPageSize;
const foundPageSizeIndex = pageSizeOptions.findIndex(
([option]) => option >= resultCurrentPageSize,
);
if (foundPageSizeIndex === -1) {
resultCurrentPageSize = 0;
}
resultCurrentPage = serverPaginationData.currentPage ?? 0;
resultOnPageChange = (pageNumber: number) =>
onServerPaginationChange(pageNumber, serverPageSize);
}
return (
<div ref={wrapperRef} style={{ width: initialWidth, height: initialHeight }}>
{hasGlobalControl ? (
Expand All @@ -254,7 +273,7 @@ export default function DataTable<D extends object>({
{hasPagination ? (
<SelectPageSize
total={resultsSize}
current={pageSize}
current={resultCurrentPageSize}
options={pageSizeOptions}
selectRenderer={typeof selectPageSize === 'boolean' ? undefined : selectPageSize}
onChange={setPageSize}
Expand All @@ -275,23 +294,14 @@ export default function DataTable<D extends object>({
</div>
) : null}
{wrapStickyTable ? wrapStickyTable(renderTable) : renderTable()}
{serverPagination && (
<ServerPagination
ref={paginationRef}
style={paginationStyle}
showNext={showNextButton}
showPrevious={currentPage !== 0}
onPageChange={goToBEPage}
/>
)}
{!serverPagination && hasPagination ? (
{hasPagination && resultPageCount > 1 ? (
<SimplePagination
ref={paginationRef}
style={paginationStyle}
maxPageItemCount={maxPageItemCount}
pageCount={pageCount}
currentPage={pageIndex}
onPageChange={gotoPage}
pageCount={resultPageCount}
currentPage={resultCurrentPage}
onPageChange={resultOnPageChange}
/>
) : null}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
* under the License.
*/
import React from 'react';
import { formatSelectOptions } from '@superset-ui/chart-controls';

export type SizeOption = number | [number, string];
export type SizeOption = [number, string];

export interface SelectPageSizeRendererProps {
current: number;
Expand Down Expand Up @@ -81,7 +82,7 @@ export default React.memo(function SelectPageSize({
options.splice(
sizeOptionValues.findIndex(x => x > currentSize),
0,
currentSize,
formatSelectOptions([currentSize])[0],
);
}
const current = currentSize === undefined ? sizeOptionValues[0] : currentSize;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { SetDataMaskHook } from '@superset-ui/core';

export const updateExternalFormData = (
setDataMask: SetDataMaskHook,
setDataMask: SetDataMaskHook = () => {},
pageNumber: number,
pageSize: number,
) =>
Expand All @@ -29,7 +29,7 @@ export const updateExternalFormData = (
extraFormData: {
custom_form_data: {
row_offset: pageNumber * pageSize,
row_limit: pageSize + 1,
row_limit: pageSize,
},
},
currentState: {
Expand Down

0 comments on commit 103f8e4

Please sign in to comment.