From c17fbdc8ea8cdb883b4e4a5e7ffac5a360e9ef99 Mon Sep 17 00:00:00 2001 From: lxk955 <164191365+lxk955@users.noreply.github.com> Date: Fri, 9 Oct 2026 09:58:00 +0800 Subject: [PATCH] fix(grid): hide bottom export menu when result has no columns --- apps/desktop/src/components/grid/DataGrid.vue | 1 + .../components/grid/DataGridPagination.vue | 5 +- .../grid/__tests__/DataGridSurfaces.spec.ts | 49 +++++++++++++++++++ .../DataGridToolbarIconTooltips.spec.ts | 19 ++++++- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index d634f77b36..59dd5c1652 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -15353,6 +15353,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val :load-all-rows-active="loadAllRowsActive" :load-all-rows-enabled="loadAllRowsEnabled" :can-load-all-rows="result.rows.length > 0" + :can-export="result.columns.length > 0" :page-size="pageSize" :default-page-size="defaultPageSize" :page-size-menu-items="pageSizeMenuItems" diff --git a/apps/desktop/src/components/grid/DataGridPagination.vue b/apps/desktop/src/components/grid/DataGridPagination.vue index 9e8ab85612..6a3a3ee4e4 100644 --- a/apps/desktop/src/components/grid/DataGridPagination.vue +++ b/apps/desktop/src/components/grid/DataGridPagination.vue @@ -23,6 +23,7 @@ const props = withDefaults( loadAllRowsActive: boolean; loadAllRowsEnabled: boolean; canLoadAllRows: boolean; + canExport?: boolean; pageSize: number; defaultPageSize: number; pageSizeMenuItems: LightDropdownItem[]; @@ -32,7 +33,7 @@ const props = withDefaults( canGoNextPage: boolean; canJumpLastPage: boolean; }>(), - { paginationEnabled: true, loadAllRowsActive: false, loadAllRowsEnabled: true, canLoadAllRows: false }, + { paginationEnabled: true, loadAllRowsActive: false, loadAllRowsEnabled: true, canLoadAllRows: false, canExport: true }, ); const customPageSizeInput = defineModel("customPageSizeInput", { default: "" }); @@ -164,6 +165,6 @@ function handlePageInputKeydown(event: KeyboardEvent) { {{ t("grid.loadAllAndGoToLastRow") }} - + diff --git a/apps/desktop/src/components/grid/__tests__/DataGridSurfaces.spec.ts b/apps/desktop/src/components/grid/__tests__/DataGridSurfaces.spec.ts index 80e2aa734e..371dfe0ede 100644 --- a/apps/desktop/src/components/grid/__tests__/DataGridSurfaces.spec.ts +++ b/apps/desktop/src/components/grid/__tests__/DataGridSurfaces.spec.ts @@ -412,6 +412,55 @@ describe("DataGridPagination", () => { await mounted.setProps({ loading: false, canLoadAllRows: false }); expect(findOne(mounted.root, (node) => node.props["aria-label"] === "grid.loadAllAndGoToLastRow").props.disabled).toBe(true); }); + + it("gates the bottom export menu based on canExport", async () => { + const mounted = mountComponent(DataGridPagination, { + selectionSummary: null, + selectionSummarySumText: "", + selectionSummaryAverageText: "", + loading: false, + infiniteScrollEnabled: false, + infiniteScrollAllLoaded: false, + pageSize: 100, + customPageSizeInput: "", + pageSizeMenuItems: [], + exportMenuItems: [{ value: "csv", label: "CSV" }], + currentPage: 1, + canGoNextPage: false, + canJumpLastPage: false, + canExport: true, + }); + + const findExportMenu = () => findAll(mounted.root, (node) => node.props["data-stub"] === "LightDropdown" && node.props["aria-label"] === "grid.export"); + expect(findExportMenu()).toHaveLength(1); + + await mounted.setProps({ canExport: false }); + expect(findExportMenu()).toHaveLength(0); + + await mounted.setProps({ canExport: true }); + expect(findExportMenu()).toHaveLength(1); + }); + + it("renders export menu by default when canExport is not specified", () => { + const mounted = mountComponent(DataGridPagination, { + selectionSummary: null, + selectionSummarySumText: "", + selectionSummaryAverageText: "", + loading: false, + infiniteScrollEnabled: false, + infiniteScrollAllLoaded: false, + pageSize: 100, + customPageSizeInput: "", + pageSizeMenuItems: [], + exportMenuItems: [{ value: "csv", label: "CSV" }], + currentPage: 1, + canGoNextPage: false, + canJumpLastPage: false, + }); + + const exportMenus = findAll(mounted.root, (node) => node.props["data-stub"] === "LightDropdown" && node.props["aria-label"] === "grid.export"); + expect(exportMenus).toHaveLength(1); + }); }); describe("DataGridColumnHeader", () => { diff --git a/apps/desktop/src/components/grid/__tests__/DataGridToolbarIconTooltips.spec.ts b/apps/desktop/src/components/grid/__tests__/DataGridToolbarIconTooltips.spec.ts index 4c7ce2317f..4911ef3b0b 100644 --- a/apps/desktop/src/components/grid/__tests__/DataGridToolbarIconTooltips.spec.ts +++ b/apps/desktop/src/components/grid/__tests__/DataGridToolbarIconTooltips.spec.ts @@ -95,7 +95,7 @@ function mountToolbar() { ); } -function mountGrid() { +function mountGrid(resultOverride?: Partial) { const pinia = createPinia(); setActivePinia(pinia); const settingsStore = useSettingsStore(); @@ -108,6 +108,7 @@ function mountGrid() { rows: [[1, "Ada"]], affected_rows: 0, execution_time_ms: 0, + ...resultOverride, }); const grid = ref<{ goToColumnToolbarCapability: DataGridToolbarActionCapability }>(); const host = document.createElement("div"); @@ -212,6 +213,22 @@ describe("data grid icon-only toolbar tooltips", () => { expect(host.querySelector("[data-column-lookup-panel]")).not.toBeNull(); expect(host.querySelector('[data-slot="popover-content"]')).toBeNull(); }); + + it("hides the bottom pagination export menu when the result has no columns", async () => { + const { host } = mountGrid({ columns: [], rows: [] }); + await settle(); + + const bottomExportMenu = Array.from(host.querySelectorAll("button")).find((btn) => btn.getAttribute("aria-label") === "Export"); + expect(bottomExportMenu).toBeUndefined(); + }); + + it("shows the bottom pagination export menu when the result has columns", async () => { + const { host } = mountGrid(); + await settle(); + + const bottomExportMenu = Array.from(host.querySelectorAll("button")).find((btn) => btn.getAttribute("aria-label") === "Export"); + expect(bottomExportMenu).not.toBeUndefined(); + }); }); // Reka resolves the popper anchor from the nearest popper root. Wrapping an overlay