This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new a77fec68d40 fix(drill-detail): make page-size selector functionally
adjustable (#37975)
a77fec68d40 is described below
commit a77fec68d405227f2dfceb176b0285d946fdfbec
Author: Varun Chawla <[email protected]>
AuthorDate: Tue May 12 13:39:41 2026 -0700
fix(drill-detail): make page-size selector functionally adjustable (#37975)
Co-authored-by: Claude Opus 4.7 <[email protected]>
Co-authored-by: Evan Rusackas <[email protected]>
---
.../Chart/DrillDetail/DrillDetailPane.test.tsx | 78 ++++++++++++++++++++++
.../Chart/DrillDetail/DrillDetailPane.tsx | 23 +++++--
2 files changed, 94 insertions(+), 7 deletions(-)
diff --git
a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.test.tsx
b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.test.tsx
index f9d9b924878..f470f18e50a 100644
---
a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.test.tsx
+++
b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.test.tsx
@@ -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();
diff --git
a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
index 9326b63b485..901f9d2421b 100644
--- a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
+++ b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
@@ -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}