mirror of
https://github.com/apache/superset.git
synced 2026-07-29 01:52:26 +00:00
feat(drill-detail): add CSV/XLSX download + reload to drill modals
Rebased onto fresh upstream master. Upstream substantially refactored
the DataTables / GridTable surface since this PR first opened, so the
previous 14 fix-commits have been collapsed into one clean change on
top of current master.
Scope (narrowed from the original PR): only the two drill modals are
affected. The Explore pane Results/Samples tabs are left as-is since
upstream moved them toward a different row-limit-selector pattern.
Layout matches the last-approved maintainer review: RowCount →
Download dropdown (CSV/XLSX) → Copy → Reload, inside the existing
action bar on both drill modals. No changes to icon position or
aesthetic from the previously approved iteration.
Drill to Detail:
- DrillDetailPane owns download logic, uses ROW_LIMIT (not
SAMPLES_ROW_LIMIT) via /api/v1/chart/data with
result_type=drill_detail.
- dashboardId included in form_data for guest/embedded auth.
- usePermissions() used so GRANULAR_EXPORT_CONTROLS is respected.
Drill By:
- DrillByModal uses exportChart with the current drilledFormData.
- Reload re-fetches chart data via getChartDataRequest.
- Handlers are passed through useResultsTableView →
SingleQueryResultPane → DataTableControls (props made optional so
explore panes are unchanged).
Supporting changes:
- New DownloadDropdown shared component (icon + CSV/XLSX dropdown).
- CopyToClipboardButton icon now uses theme.colorIcon, wrapped in
a span so CopyToClipboard's cloneElement doesn't strip the style
(keeps icon colour consistent with download/reload).
- ROW_LIMIT added to the list of configs exposed to the frontend.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ import {
|
||||
LOG_ACTIONS_FURTHER_DRILL_BY,
|
||||
} from 'src/logger/LogUtils';
|
||||
import { findPermission } from 'src/utils/findPermission';
|
||||
import { getQuerySettings } from 'src/explore/exploreUtils';
|
||||
import { getQuerySettings, exportChart } from 'src/explore/exploreUtils';
|
||||
import { isEmbedded } from 'src/dashboard/util/isEmbedded';
|
||||
import { Dataset, DrillByType } from '../types';
|
||||
import DrillByChart from './DrillByChart';
|
||||
@@ -209,12 +209,6 @@ export default function DrillByModal({
|
||||
const { displayModeToggle, drillByDisplayMode } = useDisplayModeToggle();
|
||||
const [chartDataResult, setChartDataResult] = useState<QueryData[]>();
|
||||
|
||||
const resultsTable = useResultsTableView(
|
||||
chartDataResult,
|
||||
formData.datasource,
|
||||
canDownload,
|
||||
);
|
||||
|
||||
const [currentFormData, setCurrentFormData] = useState(formData);
|
||||
const [usedGroupbyColumns, setUsedGroupbyColumns] = useState<Column[]>(
|
||||
[...initialGroupbyColumns, column].filter(isDefined),
|
||||
@@ -377,6 +371,57 @@ export default function DrillByModal({
|
||||
formData,
|
||||
]);
|
||||
|
||||
const handleDownload = useCallback(
|
||||
(exportType: 'csv' | 'xlsx') => {
|
||||
exportChart({
|
||||
formData: drilledFormData,
|
||||
resultFormat: exportType,
|
||||
resultType: 'full',
|
||||
});
|
||||
},
|
||||
[drilledFormData],
|
||||
);
|
||||
|
||||
const handleDownloadCSV = useCallback(
|
||||
() => handleDownload('csv'),
|
||||
[handleDownload],
|
||||
);
|
||||
|
||||
const handleDownloadXLSX = useCallback(
|
||||
() => handleDownload('xlsx'),
|
||||
[handleDownload],
|
||||
);
|
||||
|
||||
const handleReload = useCallback(() => {
|
||||
setChartDataResult(undefined);
|
||||
setIsChartDataLoading(true);
|
||||
const [useLegacyApi] = getQuerySettings(drilledFormData);
|
||||
getChartDataRequest({
|
||||
formData: drilledFormData,
|
||||
})
|
||||
.then(({ response, json }) =>
|
||||
handleChartDataResponse(response, json, useLegacyApi),
|
||||
)
|
||||
.then(queriesResponse => {
|
||||
setChartDataResult(queriesResponse);
|
||||
})
|
||||
.catch(() => {
|
||||
addDangerToast(t('Failed to load chart data.'));
|
||||
})
|
||||
.finally(() => {
|
||||
setIsChartDataLoading(false);
|
||||
});
|
||||
}, [addDangerToast, drilledFormData]);
|
||||
|
||||
const resultsTable = useResultsTableView(
|
||||
chartDataResult,
|
||||
formData.datasource,
|
||||
canDownload,
|
||||
handleDownloadCSV,
|
||||
handleDownloadXLSX,
|
||||
handleReload,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setUsedGroupbyColumns(usedCols =>
|
||||
!currentColumn ||
|
||||
|
||||
@@ -35,6 +35,9 @@ export const useResultsTableView = (
|
||||
chartDataResult: QueryData[] | undefined,
|
||||
datasourceId: string,
|
||||
canDownload: boolean,
|
||||
onDownloadCSV?: () => void,
|
||||
onDownloadXLSX?: () => void,
|
||||
onReload?: () => void,
|
||||
) => {
|
||||
if (!isDefined(chartDataResult)) {
|
||||
return <div />;
|
||||
@@ -50,6 +53,9 @@ export const useResultsTableView = (
|
||||
datasourceId={datasourceId}
|
||||
isVisible
|
||||
canDownload={canDownload}
|
||||
onDownloadCSV={onDownloadCSV}
|
||||
onDownloadXLSX={onDownloadXLSX}
|
||||
onReload={onReload}
|
||||
/>
|
||||
</ResultContainer>
|
||||
);
|
||||
@@ -70,6 +76,9 @@ export const useResultsTableView = (
|
||||
datasourceId={datasourceId}
|
||||
isVisible
|
||||
canDownload={canDownload}
|
||||
onDownloadCSV={onDownloadCSV}
|
||||
onDownloadXLSX={onDownloadXLSX}
|
||||
onReload={onReload}
|
||||
/>
|
||||
</ResultContainer>
|
||||
),
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { css, useTheme } from '@apache-superset/core/theme';
|
||||
import { Dropdown, Tooltip } from '@superset-ui/core/components';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
|
||||
interface DownloadDropdownProps {
|
||||
onDownloadCSV: () => void;
|
||||
onDownloadXLSX: () => void;
|
||||
}
|
||||
|
||||
const DownloadDropdown = ({
|
||||
onDownloadCSV,
|
||||
onDownloadXLSX,
|
||||
}: DownloadDropdownProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
menu={{
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'csv') {
|
||||
onDownloadCSV();
|
||||
} else if (key === 'xlsx') {
|
||||
onDownloadXLSX();
|
||||
}
|
||||
},
|
||||
items: [
|
||||
{
|
||||
key: 'csv',
|
||||
label: t('Export to CSV'),
|
||||
icon: <Icons.FileOutlined />,
|
||||
},
|
||||
{
|
||||
key: 'xlsx',
|
||||
label: t('Export to Excel'),
|
||||
icon: <Icons.FileOutlined />,
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Tooltip title={t('Download')}>
|
||||
<span
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
aria-label={t('Download')}
|
||||
data-test="drill-detail-download-btn"
|
||||
>
|
||||
<Icons.DownloadOutlined
|
||||
iconColor={theme.colorIcon}
|
||||
iconSize="l"
|
||||
css={css`
|
||||
&.anticon > * {
|
||||
line-height: 0;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadDropdown;
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
ensureIsArray,
|
||||
JsonObject,
|
||||
QueryFormData,
|
||||
SupersetClient,
|
||||
} from '@superset-ui/core';
|
||||
import { css, useTheme } from '@apache-superset/core/theme';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
@@ -47,6 +48,10 @@ import Table, {
|
||||
TableSize,
|
||||
} from '@superset-ui/core/components/Table';
|
||||
import { RootState } from 'src/dashboard/types';
|
||||
import { usePermissions } from 'src/hooks/usePermissions';
|
||||
import { useToasts } from 'src/components/MessageToasts/withToasts';
|
||||
import { ensureAppRoot } from 'src/utils/pathUtils';
|
||||
import { safeStringify } from 'src/utils/safeStringify';
|
||||
import HeaderWithRadioGroup from '@superset-ui/core/components/Table/header-renderers/HeaderWithRadioGroup';
|
||||
import { useDatasetMetadataBar } from 'src/features/datasets/metadataBar/useDatasetMetadataBar';
|
||||
import { Dataset } from '../types';
|
||||
@@ -108,6 +113,13 @@ export default function DrillDetailPane({
|
||||
state.common.conf.SAMPLES_ROW_LIMIT,
|
||||
);
|
||||
|
||||
const ROW_LIMIT = useSelector(
|
||||
(state: { common: { conf: JsonObject } }) => state.common.conf.ROW_LIMIT,
|
||||
);
|
||||
|
||||
const { canDownload } = usePermissions();
|
||||
const { addDangerToast } = useToasts();
|
||||
|
||||
// Extract datasource ID/type from string ID
|
||||
const [datasourceId, datasourceType] = useMemo(
|
||||
() => formData.datasource.split('__'),
|
||||
@@ -207,6 +219,64 @@ export default function DrillDetailPane({
|
||||
setPageIndex(0);
|
||||
}, []);
|
||||
|
||||
const handleDownload = useCallback(
|
||||
(exportType: 'csv' | 'xlsx') => {
|
||||
const drillPayload = getDrillPayload(formData, filters);
|
||||
if (!drillPayload) {
|
||||
addDangerToast(t('Unable to generate download payload'));
|
||||
return;
|
||||
}
|
||||
const payload: JsonObject = {
|
||||
datasource: {
|
||||
id: parseInt(datasourceId, 10),
|
||||
type: datasourceType,
|
||||
},
|
||||
queries: [
|
||||
{
|
||||
...drillPayload,
|
||||
columns: [],
|
||||
metrics: [],
|
||||
orderby: [],
|
||||
row_limit: ROW_LIMIT,
|
||||
row_offset: 0,
|
||||
},
|
||||
],
|
||||
result_type: 'drill_detail',
|
||||
result_format: exportType,
|
||||
force: false,
|
||||
};
|
||||
if (dashboardId) {
|
||||
payload.form_data = { dashboardId };
|
||||
}
|
||||
SupersetClient.postForm(ensureAppRoot('/api/v1/chart/data'), {
|
||||
form_data: safeStringify(payload),
|
||||
}).catch(error => {
|
||||
addDangerToast(
|
||||
t('Failed to generate download: %s', error.message || error),
|
||||
);
|
||||
});
|
||||
},
|
||||
[
|
||||
formData,
|
||||
filters,
|
||||
datasourceId,
|
||||
datasourceType,
|
||||
ROW_LIMIT,
|
||||
dashboardId,
|
||||
addDangerToast,
|
||||
],
|
||||
);
|
||||
|
||||
const handleDownloadCSV = useCallback(
|
||||
() => handleDownload('csv'),
|
||||
[handleDownload],
|
||||
);
|
||||
|
||||
const handleDownloadXLSX = useCallback(
|
||||
() => handleDownload('xlsx'),
|
||||
[handleDownload],
|
||||
);
|
||||
|
||||
// Clear cache and reset page index if filters change
|
||||
useEffect(() => {
|
||||
setResponseError('');
|
||||
@@ -338,6 +408,11 @@ export default function DrillDetailPane({
|
||||
totalCount={resultsPage?.total}
|
||||
loading={isLoading}
|
||||
onReload={handleReload}
|
||||
canDownload={canDownload}
|
||||
onDownloadCSV={handleDownloadCSV}
|
||||
onDownloadXLSX={handleDownloadXLSX}
|
||||
data={data}
|
||||
columnNames={resultsPage?.colNames}
|
||||
/>
|
||||
)}
|
||||
{tableContent}
|
||||
|
||||
@@ -33,4 +33,9 @@ InteractiveTableControls.args = {
|
||||
{ op: '>', col: 'tz_offset', val: 200 },
|
||||
{ op: '==', col: 'platform', val: 'GB' },
|
||||
],
|
||||
canDownload: true,
|
||||
onDownloadCSV: () => {},
|
||||
onDownloadXLSX: () => {},
|
||||
onReload: () => {},
|
||||
loading: false,
|
||||
};
|
||||
|
||||
@@ -21,6 +21,8 @@ import TableControls from './DrillDetailTableControls';
|
||||
|
||||
const setFilters = jest.fn();
|
||||
const onReload = jest.fn();
|
||||
const onDownloadCSV = jest.fn();
|
||||
const onDownloadXLSX = jest.fn();
|
||||
const setup = (overrides: Record<string, any> = {}) => {
|
||||
const props = {
|
||||
filters: [],
|
||||
@@ -28,9 +30,14 @@ const setup = (overrides: Record<string, any> = {}) => {
|
||||
onReload,
|
||||
loading: false,
|
||||
totalCount: 0,
|
||||
canDownload: true,
|
||||
onDownloadCSV,
|
||||
onDownloadXLSX,
|
||||
data: [],
|
||||
columnNames: [],
|
||||
...overrides,
|
||||
};
|
||||
return render(<TableControls {...props} />);
|
||||
return render(<TableControls {...props} />, { useRedux: true });
|
||||
};
|
||||
test('should render', () => {
|
||||
const { container } = setup();
|
||||
|
||||
@@ -27,6 +27,9 @@ import {
|
||||
import { css, useTheme } from '@apache-superset/core/theme';
|
||||
import RowCountLabel from 'src/components/RowCountLabel';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import { Tooltip } from '@superset-ui/core/components';
|
||||
import { CopyToClipboardButton } from 'src/explore/components/DataTableControl';
|
||||
import DownloadDropdown from './DownloadDropdown';
|
||||
|
||||
export type TableControlsProps = {
|
||||
filters: BinaryQueryObjectFilterClause[];
|
||||
@@ -34,6 +37,11 @@ export type TableControlsProps = {
|
||||
totalCount?: number;
|
||||
loading: boolean;
|
||||
onReload: () => void;
|
||||
canDownload: boolean;
|
||||
onDownloadCSV: () => void;
|
||||
onDownloadXLSX: () => void;
|
||||
data?: Record<string, any>[];
|
||||
columnNames?: string[];
|
||||
};
|
||||
|
||||
export default function TableControls({
|
||||
@@ -42,6 +50,11 @@ export default function TableControls({
|
||||
totalCount,
|
||||
loading,
|
||||
onReload,
|
||||
canDownload,
|
||||
onDownloadCSV,
|
||||
onDownloadXLSX,
|
||||
data,
|
||||
columnNames,
|
||||
}: TableControlsProps) {
|
||||
const theme = useTheme();
|
||||
const filterMap: Record<string, BinaryQueryObjectFilterClause> = useMemo(
|
||||
@@ -118,16 +131,28 @@ export default function TableControls({
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: min-content;
|
||||
gap: ${theme.sizeUnit * 3}px;
|
||||
`}
|
||||
>
|
||||
<RowCountLabel loading={loading && !totalCount} rowcount={totalCount} />
|
||||
<Icons.ReloadOutlined
|
||||
iconColor={theme.colorIcon}
|
||||
iconSize="l"
|
||||
aria-label={t('Reload')}
|
||||
role="button"
|
||||
onClick={onReload}
|
||||
/>
|
||||
{canDownload && (
|
||||
<DownloadDropdown
|
||||
onDownloadCSV={onDownloadCSV}
|
||||
onDownloadXLSX={onDownloadXLSX}
|
||||
/>
|
||||
)}
|
||||
{canDownload && (
|
||||
<CopyToClipboardButton data={data} columns={columnNames} />
|
||||
)}
|
||||
<Tooltip title={t('Reload')}>
|
||||
<Icons.ReloadOutlined
|
||||
iconColor={theme.colorIcon}
|
||||
iconSize="l"
|
||||
aria-label={t('Reload')}
|
||||
role="button"
|
||||
onClick={onReload}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -63,33 +63,40 @@ export const CopyToClipboardButton = ({
|
||||
data?: TabularDataRow[];
|
||||
columns?: string[];
|
||||
disabled?: boolean;
|
||||
}) => (
|
||||
<CopyToClipboard
|
||||
text={
|
||||
!disabled && data && columns
|
||||
? prepareCopyToClipboardTabularData(data, columns)
|
||||
: ''
|
||||
}
|
||||
disabled={disabled}
|
||||
wrapped={false}
|
||||
copyNode={
|
||||
<Icons.CopyOutlined
|
||||
iconSize="l"
|
||||
aria-label={t('Copy')}
|
||||
aria-disabled={disabled}
|
||||
role="button"
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
css={css`
|
||||
opacity: ${disabled ? 0.3 : 1};
|
||||
cursor: ${disabled ? 'not-allowed' : 'pointer'};
|
||||
&.anticon > * {
|
||||
line-height: 0;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<CopyToClipboard
|
||||
text={
|
||||
!disabled && data && columns
|
||||
? prepareCopyToClipboardTabularData(data, columns)
|
||||
: ''
|
||||
}
|
||||
disabled={disabled}
|
||||
wrapped={false}
|
||||
copyNode={
|
||||
<span
|
||||
role="button"
|
||||
aria-label={t('Copy')}
|
||||
aria-disabled={disabled}
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
>
|
||||
<Icons.CopyOutlined
|
||||
iconColor={theme.colorIcon}
|
||||
iconSize="l"
|
||||
css={css`
|
||||
opacity: ${disabled ? 0.3 : 1};
|
||||
cursor: ${disabled ? 'not-allowed' : 'pointer'};
|
||||
&.anticon > * {
|
||||
line-height: 0;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const FilterInput = ({
|
||||
onChangeHandler,
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { styled, css } from '@apache-superset/core/theme';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import { styled, css, useTheme } from '@apache-superset/core/theme';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import { useMemo } from 'react';
|
||||
import { zip } from 'lodash';
|
||||
import { Tooltip } from '@superset-ui/core/components';
|
||||
import { Select } from 'antd';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import {
|
||||
CopyToClipboardButton,
|
||||
FilterInput,
|
||||
@@ -31,6 +32,7 @@ import { applyFormattingToTabularData } from 'src/utils/common';
|
||||
import { getTimeColumns } from 'src/explore/components/DataTableControl/utils';
|
||||
import RowCountLabel from 'src/components/RowCountLabel';
|
||||
import { usePermissions } from 'src/hooks/usePermissions';
|
||||
import DownloadDropdown from 'src/components/Chart/DrillDetail/DownloadDropdown';
|
||||
import { TableControlsProps } from '../types';
|
||||
|
||||
export const ROW_LIMIT_OPTIONS = [
|
||||
@@ -63,10 +65,15 @@ export const TableControls = ({
|
||||
columnTypes,
|
||||
rowcount,
|
||||
isLoading,
|
||||
canDownload,
|
||||
rowLimit,
|
||||
rowLimitOptions,
|
||||
onRowLimitChange,
|
||||
onDownloadCSV,
|
||||
onDownloadXLSX,
|
||||
onReload,
|
||||
}: TableControlsProps) => {
|
||||
const theme = useTheme();
|
||||
const originalTimeColumns = getTimeColumns(datasourceId);
|
||||
const formattedTimeColumns = zip<string, GenericDataType>(
|
||||
columnNames,
|
||||
@@ -109,6 +116,12 @@ export const TableControls = ({
|
||||
{(!onRowLimitChange || rowcount < (rowLimit ?? Infinity)) && (
|
||||
<RowCountLabel rowcount={rowcount} loading={isLoading} />
|
||||
)}
|
||||
{canDownload && onDownloadCSV && onDownloadXLSX && (
|
||||
<DownloadDropdown
|
||||
onDownloadCSV={onDownloadCSV}
|
||||
onDownloadXLSX={onDownloadXLSX}
|
||||
/>
|
||||
)}
|
||||
{copyEnabled ? (
|
||||
<CopyToClipboardButton data={formattedData} columns={columnNames} />
|
||||
) : (
|
||||
@@ -122,6 +135,17 @@ export const TableControls = ({
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{onReload && (
|
||||
<Tooltip title={t('Reload')}>
|
||||
<Icons.ReloadOutlined
|
||||
iconColor={theme.colorIcon}
|
||||
iconSize="l"
|
||||
aria-label={t('Reload')}
|
||||
role="button"
|
||||
onClick={onReload}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</TableControlsWrapper>
|
||||
);
|
||||
|
||||
@@ -57,6 +57,9 @@ export const SingleQueryResultPane = ({
|
||||
rowLimit,
|
||||
rowLimitOptions,
|
||||
onRowLimitChange,
|
||||
onDownloadCSV,
|
||||
onDownloadXLSX,
|
||||
onReload,
|
||||
}: SingleQueryResultPaneProp) => {
|
||||
const [filterText, setFilterText] = useState('');
|
||||
const { gridHeight, measuredRef } = useGridHeight();
|
||||
@@ -83,6 +86,9 @@ export const SingleQueryResultPane = ({
|
||||
rowLimit={rowLimit}
|
||||
rowLimitOptions={rowLimitOptions}
|
||||
onRowLimitChange={onRowLimitChange}
|
||||
onDownloadCSV={onDownloadCSV}
|
||||
onDownloadXLSX={onDownloadXLSX}
|
||||
onReload={onReload}
|
||||
/>
|
||||
<GridContainer>
|
||||
<GridSizer ref={measuredRef}>
|
||||
|
||||
@@ -76,6 +76,9 @@ export interface TableControlsProps {
|
||||
rowLimit?: number;
|
||||
rowLimitOptions?: { value: number; label: string }[];
|
||||
onRowLimitChange?: (limit: number) => void;
|
||||
onDownloadCSV?: () => void;
|
||||
onDownloadXLSX?: () => void;
|
||||
onReload?: () => void;
|
||||
}
|
||||
|
||||
export interface QueryResultInterface {
|
||||
@@ -95,4 +98,7 @@ export interface SingleQueryResultPaneProp extends QueryResultInterface {
|
||||
rowLimit?: number;
|
||||
rowLimitOptions?: { value: number; label: string }[];
|
||||
onRowLimitChange?: (limit: number) => void;
|
||||
onDownloadCSV?: () => void;
|
||||
onDownloadXLSX?: () => void;
|
||||
onReload?: () => void;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ FRONTEND_CONF_KEYS = (
|
||||
"COLUMNAR_EXTENSIONS",
|
||||
"ALLOWED_EXTENSIONS",
|
||||
"SAMPLES_ROW_LIMIT",
|
||||
"ROW_LIMIT",
|
||||
"DEFAULT_TIME_FILTER",
|
||||
"HTML_SANITIZATION",
|
||||
"HTML_SANITIZATION_SCHEMA_EXTENSIONS",
|
||||
|
||||
Reference in New Issue
Block a user