mirror of
https://github.com/apache/superset.git
synced 2026-05-21 15:55:10 +00:00
fix(drill-detail): make page-size selector functionally adjustable (#37975)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Evan Rusackas <evan@preset.io>
This commit is contained in:
@@ -19,10 +19,12 @@
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { QueryFormData, SupersetClient } from '@superset-ui/core';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
userEvent,
|
||||
waitFor,
|
||||
within,
|
||||
} from 'spec/helpers/testing-library';
|
||||
import { getMockStoreWithNativeFilters } from 'spec/fixtures/mockStore';
|
||||
import chartQueries, { sliceId } from 'spec/fixtures/mockChartQueries';
|
||||
@@ -119,6 +121,34 @@ const fetchWithData = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const fetchWithPaginatedData = () => {
|
||||
setupDatasetEndpoint();
|
||||
fetchMock.post(SAMPLES_ENDPOINT, {
|
||||
result: {
|
||||
total_count: 100,
|
||||
data: [
|
||||
{
|
||||
year: 1996,
|
||||
na_sales: 11.27,
|
||||
eu_sales: 8.89,
|
||||
},
|
||||
{
|
||||
year: 1989,
|
||||
na_sales: 23.2,
|
||||
eu_sales: 2.26,
|
||||
},
|
||||
{
|
||||
year: 1999,
|
||||
na_sales: 9,
|
||||
eu_sales: 6.18,
|
||||
},
|
||||
],
|
||||
colnames: ['year', 'na_sales', 'eu_sales'],
|
||||
coltypes: [0, 0, 0],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.clearHistory().removeRoutes();
|
||||
supersetGetCache.clear();
|
||||
@@ -254,6 +284,54 @@ describe('download actions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should render pagination when results exceed page size', async () => {
|
||||
// The "should render the error" test above leaves a SupersetClient.post
|
||||
// rejection spy active (matching the existing pattern; "should use
|
||||
// verbose_map" further down does the same cleanup). Reset it here so the
|
||||
// fetch in this test actually returns data.
|
||||
jest.restoreAllMocks();
|
||||
fetchWithPaginatedData();
|
||||
await waitForRender();
|
||||
// With total_count=100 and page size=50, pagination should render
|
||||
await waitFor(() => {
|
||||
const pagination = document.querySelector('.ant-pagination');
|
||||
expect(pagination).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
test('should offer the full set of page-size options', async () => {
|
||||
fetchWithPaginatedData();
|
||||
await waitForRender();
|
||||
|
||||
// The page-size changer renders as an antd Select. In jsdom, antd opens
|
||||
// its overlay on mouseDown of the .ant-select-selector element rather
|
||||
// than via a click on the inner combobox input.
|
||||
const selector = await waitFor(() => {
|
||||
const el = document.querySelector(
|
||||
'.ant-pagination-options-size-changer .ant-select-selector',
|
||||
) as HTMLElement | null;
|
||||
expect(el).toBeTruthy();
|
||||
return el!;
|
||||
});
|
||||
fireEvent.mouseDown(selector);
|
||||
|
||||
// The opened listbox lives in a body portal; collect its options and assert
|
||||
// exactly the canonical [5, 15, 25, 50, 100] set is offered. Without this
|
||||
// guard, regressing to a single hardcoded option (the pre-rework approach)
|
||||
// would silently pass CI.
|
||||
const listbox = await screen.findByRole('listbox');
|
||||
const offeredSizes = within(listbox)
|
||||
.getAllByRole('option')
|
||||
.map(el => el.getAttribute('title'));
|
||||
expect(offeredSizes).toEqual([
|
||||
'5 / page',
|
||||
'15 / page',
|
||||
'25 / page',
|
||||
'50 / page',
|
||||
'100 / page',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should use verbose_map for column headers when available', async () => {
|
||||
jest.restoreAllMocks();
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ import { getDrillPayload } from './utils';
|
||||
import { ResultsPage } from './types';
|
||||
import { datasetLabelLower } from 'src/features/semanticLayers/label';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
const DEFAULT_PAGE_SIZE = 50;
|
||||
|
||||
interface DataType {
|
||||
[key: string]: any;
|
||||
@@ -94,6 +94,7 @@ export default function DrillDetailPane({
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||
const lastPageIndex = useRef(pageIndex);
|
||||
const [filters, setFilters] = useState(initialFilters);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -307,13 +308,13 @@ export default function DrillDetailPane({
|
||||
if (!responseError && !isLoading && !resultsPages.has(pageIndex)) {
|
||||
setIsLoading(true);
|
||||
const jsonPayload = getDrillPayload(formData, filters) ?? {};
|
||||
const cachePageLimit = Math.ceil(SAMPLES_ROW_LIMIT / PAGE_SIZE);
|
||||
const cachePageLimit = Math.ceil(SAMPLES_ROW_LIMIT / pageSize);
|
||||
getDatasourceSamples(
|
||||
datasourceType as DatasourceType,
|
||||
Number(datasourceId),
|
||||
false,
|
||||
jsonPayload,
|
||||
PAGE_SIZE,
|
||||
pageSize,
|
||||
pageIndex + 1,
|
||||
dashboardId,
|
||||
)
|
||||
@@ -349,6 +350,7 @@ export default function DrillDetailPane({
|
||||
formData,
|
||||
isLoading,
|
||||
pageIndex,
|
||||
pageSize,
|
||||
responseError,
|
||||
resultsPages,
|
||||
]);
|
||||
@@ -384,13 +386,20 @@ export default function DrillDetailPane({
|
||||
data={data}
|
||||
columns={mappedColumns}
|
||||
size={TableSize.Small}
|
||||
defaultPageSize={PAGE_SIZE}
|
||||
defaultPageSize={DEFAULT_PAGE_SIZE}
|
||||
recordCount={resultsPage?.total}
|
||||
usePagination
|
||||
loading={isLoading}
|
||||
onChange={pagination =>
|
||||
setPageIndex(pagination.current ? pagination.current - 1 : 0)
|
||||
}
|
||||
onChange={pagination => {
|
||||
const newPageSize = pagination.pageSize ?? pageSize;
|
||||
if (newPageSize !== pageSize) {
|
||||
setPageSize(newPageSize);
|
||||
setResultsPages(new Map());
|
||||
setPageIndex(0);
|
||||
} else {
|
||||
setPageIndex(pagination.current ? pagination.current - 1 : 0);
|
||||
}
|
||||
}}
|
||||
resizable
|
||||
virtualize
|
||||
allowHTML={allowHTML}
|
||||
|
||||
Reference in New Issue
Block a user