From fead42e8e225dbf318da47e2d5476701d6eee055 Mon Sep 17 00:00:00 2001 From: Bhargav Date: Tue, 27 Oct 2026 10:13:11 +0000 Subject: [PATCH] fix(dashboards): PromQL table footer follows the table's page size and pagination (#15877) Co-authored-by: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com> --- .../dashboard-table-pagination.spec.js | 23 ++++++----- .../panels/PromQLTableChart.spec.ts | 41 +++++++++++++++++++ .../dashboards/panels/PromQLTableChart.vue | 11 ++--- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/tests/ui-testing/playwright-tests/Dashboards/dashboard-table-pagination.spec.js b/tests/ui-testing/playwright-tests/Dashboards/dashboard-table-pagination.spec.js index 93b5115fce..6e8095bb19 100644 --- a/tests/ui-testing/playwright-tests/Dashboards/dashboard-table-pagination.spec.js +++ b/tests/ui-testing/playwright-tests/Dashboards/dashboard-table-pagination.spec.js @@ -1113,11 +1113,9 @@ test.describe("Dashboard Table Chart Pagination Feature - PromQL Tables", () => const queryEditor = pm.chartTypeSelector.queryEditor; await queryEditor.waitFor({ state: "visible", timeout: 10000 }); - // Focus on the editor and enter a PromQL query using keyboard.type for reliable Monaco input + // Fill the Monaco input directly; typing lets autocomplete corrupt the query await queryEditor.getByRole('code').click(); - await page.keyboard.press('Control+a'); - await page.keyboard.type('cpu_usage{}'); - await page.keyboard.press('Escape'); // Dismiss any Monaco autocomplete suggestions + await queryEditor.locator('.inputarea').fill('cpu_usage{}'); // Apply await pm.dashboardPanelActions.applyAndWaitForPromQLQuery(); @@ -1155,14 +1153,19 @@ test.describe("Dashboard Table Chart Pagination Feature - PromQL Tables", () => const paginationInfo = pm.dashboardPanelConfigs.tableRowCount; await expect(paginationInfo).toHaveText(/^\s*\d+-\d+\s+of\s+\d+\s*$/, { timeout: 15000 }); - // Only the footer's presence and range shape are checked; the page-size count is wrong today (#15544) and its fix adds the exact assertion. - const paginationText = await paginationInfo.textContent(); - expect(paginationText.trim()).toMatch(/^\d+-\d+\s+of\s+\d+$/); + // The page size must bound the range: "1-10 of N" with N > 10, never "1-N of N" (#15544). + const paginationText = (await paginationInfo.textContent()).trim(); + const match = paginationText.match(/^1-(\d+)\s+of\s+(\d+)$/); + expect(match, `unexpected footer text "${paginationText}"`).not.toBeNull(); + expect(Number(match[1])).toBe(10); + expect(Number(match[2])).toBeGreaterThanOrEqual(12); - // Verify we're on the first page (starts with "1-") - expect(paginationText.trim().startsWith('1-')).toBe(true); + const nextPage = page.locator('[data-test="dashboard-table-pagination-next-page"]'); + await expect(nextPage).toBeVisible(); + await nextPage.click(); + await expect(paginationInfo).toHaveText(new RegExp(`^\\s*11-\\d+\\s+of\\s+${match[2]}\\s*$`)); - testLogger.info(`Verified PromQL table pagination: ${paginationText.trim()}`); + testLogger.info(`Verified PromQL table pagination: ${paginationText}`); // Clean up - save panel first before navigating back await pm.dashboardPanelActions.savePanel(); diff --git a/web/src/components/dashboards/panels/PromQLTableChart.spec.ts b/web/src/components/dashboards/panels/PromQLTableChart.spec.ts index 25e4f6ba0b..a34a72af21 100644 --- a/web/src/components/dashboards/panels/PromQLTableChart.spec.ts +++ b/web/src/components/dashboards/panels/PromQLTableChart.spec.ts @@ -430,6 +430,47 @@ describe("PromQLTableChart", () => { }); }); + describe("Legend footer pagination", () => { + const twelveRows = Array.from({ length: 12 }, (_, i) => ({ + __legend__: "series1", + timestamp: `2023-01-01T00:${String(i).padStart(2, "0")}:00Z`, + value: i, + })); + + const mountPaginated = () => + createWrapper({ + data: { columns: mockTableData.columns, rows: twelveRows }, + config: { ...mockConfig, table_pagination: true, table_pagination_rows_per_page: 10 }, + }); + + it("shows the page range, page size and navigation from the table's pagination state", async () => { + wrapper = mountPaginated(); + await flushPromises(); + + expect(wrapper.find('[data-test="dashboard-table-row-count"]').text()).toBe("1-10 of 12"); + expect(wrapper.find('[data-test="dashboard-table-pagination-next-page"]').exists()).toBe( + true, + ); + expect(wrapper.find('[data-test="dashboard-table-pagination-last-page"]').exists()).toBe( + true, + ); + expect(wrapper.find('[data-test="dashboard-table-rows-per-page-select"]').text()).toContain( + "10", + ); + }); + + it("reaches the remaining rows on the next page", async () => { + wrapper = mountPaginated(); + await flushPromises(); + + await wrapper.find('[data-test="dashboard-table-pagination-next-page"]').trigger("click"); + await flushPromises(); + + expect(wrapper.find('[data-test="dashboard-table-row-count"]').text()).toBe("11-12 of 12"); + expect(wrapper.findAll('tbody tr[data-test^="o2-table-row-"]').length).toBe(2); + }); + }); + describe("Event Handling", () => { it("should emit row-click when TableRenderer emits row-click", async () => { wrapper = createWrapper(); diff --git a/web/src/components/dashboards/panels/PromQLTableChart.vue b/web/src/components/dashboards/panels/PromQLTableChart.vue index bb0c529330..61c30f2d3e 100644 --- a/web/src/components/dashboards/panels/PromQLTableChart.vue +++ b/web/src/components/dashboards/panels/PromQLTableChart.vue @@ -65,13 +65,12 @@ along with this program. If not, see .