Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dashboard page xlsx export #24005

Merged
merged 6 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ const SliceHeader: FC<SliceHeaderProps> = ({
logExploreChart = () => ({}),
logEvent,
exportCSV = () => ({}),
exportXLSX = () => ({}),
editMode = false,
annotationQuery = {},
annotationError = {},
Expand Down Expand Up @@ -264,6 +265,7 @@ const SliceHeader: FC<SliceHeaderProps> = ({
logEvent={logEvent}
exportCSV={exportCSV}
exportFullCSV={exportFullCSV}
exportXLSX={exportXLSX}
supersetCanExplore={supersetCanExplore}
supersetCanShare={supersetCanShare}
supersetCanCSV={supersetCanCSV}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const createProps = (viz_type = 'sunburst') =>
exploreChart: jest.fn(),
exportCSV: jest.fn(),
exportFullCSV: jest.fn(),
exportXLSX: jest.fn(),
forceRefresh: jest.fn(),
handleToggleFullSize: jest.fn(),
toggleExpandSlice: jest.fn(),
Expand Down Expand Up @@ -126,6 +127,8 @@ test('Should render default props', () => {
// @ts-ignore
delete props.exportCSV;
// @ts-ignore
delete props.exportXLSX;
// @ts-ignore
delete props.cachedDttm;
// @ts-ignore
delete props.updatedDttm;
Expand Down Expand Up @@ -170,6 +173,16 @@ test('Should "export to CSV"', async () => {
expect(props.exportCSV).toBeCalledWith(371);
});

test('Should "export to Excel"', async () => {
const props = createProps();
renderWrapper(props);
expect(props.exportXLSX).toBeCalledTimes(0);
userEvent.hover(screen.getByText('Download'));
userEvent.click(await screen.findByText('Export to Excel'));
expect(props.exportXLSX).toBeCalledTimes(1);
expect(props.exportXLSX).toBeCalledWith(371);
});

test('Should not show "Download" if slice is filter box', () => {
const props = createProps('filter_box');
renderWrapper(props);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const MENU_KEYS = {
EXPLORE_CHART: 'explore_chart',
EXPORT_CSV: 'export_csv',
EXPORT_FULL_CSV: 'export_full_csv',
EXPORT_XLSX: 'export_xlsx',
FORCE_REFRESH: 'force_refresh',
FULLSCREEN: 'fullscreen',
TOGGLE_CHART_DESCRIPTION: 'toggle_chart_description',
Expand Down Expand Up @@ -144,6 +145,7 @@ export interface SliceHeaderControlsProps {
toggleExpandSlice?: (sliceId: number) => void;
exportCSV?: (sliceId: number) => void;
exportFullCSV?: (sliceId: number) => void;
exportXLSX?: (sliceId: number) => void;
handleToggleFullSize: () => void;

addDangerToast: (message: string) => void;
Expand Down Expand Up @@ -294,6 +296,10 @@ const SliceHeaderControls = (props: SliceHeaderControlsPropsWithRouter) => {
// eslint-disable-next-line no-unused-expressions
props.exportFullCSV?.(props.slice.slice_id);
break;
case MENU_KEYS.EXPORT_XLSX:
// eslint-disable-next-line no-unused-expressions
props.exportXLSX?.(props.slice.slice_id);
break;
case MENU_KEYS.DOWNLOAD_AS_IMAGE: {
// menu closes with a delay, we need to hide it manually,
// so that we don't capture it on the screenshot
Expand Down Expand Up @@ -493,6 +499,12 @@ const SliceHeaderControls = (props: SliceHeaderControlsPropsWithRouter) => {
</Menu.Item>
)}

<Menu.Item
key={MENU_KEYS.EXPORT_XLSX}
icon={<Icons.FileOutlined css={dropdownIconsStyles} />}
>
{t('Export to Excel')}
</Menu.Item>
<Menu.Item
key={MENU_KEYS.DOWNLOAD_AS_IMAGE}
icon={<Icons.FileImageOutlined css={dropdownIconsStyles} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
LOG_ACTIONS_CHANGE_DASHBOARD_FILTER,
LOG_ACTIONS_EXPLORE_DASHBOARD_CHART,
LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART,
LOG_ACTIONS_EXPORT_XLSX_DASHBOARD_CHART,
LOG_ACTIONS_FORCE_REFRESH_CHART,
} from 'src/logger/LogUtils';
import { areObjectsEqual } from 'src/reduxUtils';
Expand Down Expand Up @@ -139,6 +140,7 @@ class Chart extends React.Component {
this.handleFilterMenuClose = this.handleFilterMenuClose.bind(this);
this.exportCSV = this.exportCSV.bind(this);
this.exportFullCSV = this.exportFullCSV.bind(this);
this.exportXLSX = this.exportXLSX.bind(this);
this.forceRefresh = this.forceRefresh.bind(this);
this.resize = this.resize.bind(this);
this.setDescriptionRef = this.setDescriptionRef.bind(this);
Expand Down Expand Up @@ -324,8 +326,24 @@ class Chart extends React.Component {
}
};

exportFullCSV() {
this.exportCSV(true);
}

exportCSV(isFullCSV = false) {
this.props.logEvent(LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART, {
this.exportTable('csv', isFullCSV);
}

exportXLSX() {
this.exportTable('xlsx', false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is totally fine for now, but just wondering if folks will also want to do a "Full Excel" export like we do with CSV (when that feature flag is enabled). I'm totally fine skipping it for now, since it might cause performance issues, but it just made me wonder ¯\_(ツ)_/¯

Copy link
Member

@rusackas rusackas May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I'm on the topic, I wonder if the logger should also discern between regular and "full" CSV exports, in case anyone is trying to discern performance impact. Maybe an additional log for full CSV exports?

Again, not the point of this PR, but mentioning the bycatch...

}

exportTable(format, isFullCSV) {
const logAction =
format === 'csv'
? LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART
: LOG_ACTIONS_EXPORT_XLSX_DASHBOARD_CHART;
this.props.logEvent(logAction, {
slice_id: this.props.slice.slice_id,
is_cached: this.props.isCached,
});
Expand All @@ -334,16 +352,12 @@ class Chart extends React.Component {
? { ...this.props.formData, row_limit: this.props.maxRows }
: this.props.formData,
resultType: 'full',
resultFormat: 'csv',
resultFormat: format,
force: true,
ownState: this.props.ownState,
});
}

exportFullCSV() {
this.exportCSV(true);
}

forceRefresh() {
this.props.logEvent(LOG_ACTIONS_FORCE_REFRESH_CHART, {
slice_id: this.props.slice.slice_id,
Expand Down Expand Up @@ -437,6 +451,7 @@ class Chart extends React.Component {
logEvent={logEvent}
onExploreChart={this.onExploreChart}
exportCSV={this.exportCSV}
exportXLSX={this.exportXLSX}
exportFullCSV={this.exportFullCSV}
updateSliceName={updateSliceName}
sliceName={sliceName}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('Chart', () => {
addDangerToast() {},
exportCSV() {},
exportFullCSV() {},
exportXLSX() {},
componentId: 'test',
dashboardId: 111,
editMode: false,
Expand Down Expand Up @@ -145,4 +146,20 @@ describe('Chart', () => {
expect(stubbedExportCSV.lastCall.args[0].formData.row_limit).toEqual(666);
exploreUtils.exportChart.restore();
});
it('should call exportChart when exportXLSX is clicked', () => {
const stubbedExportXLSX = sinon
.stub(exploreUtils, 'exportChart')
.returns(() => {});
const wrapper = setup();
wrapper.instance().exportXLSX(props.slice.sliceId);
expect(stubbedExportXLSX.calledOnce).toBe(true);
expect(stubbedExportXLSX.lastCall.args[0]).toEqual(
expect.objectContaining({
formData: expect.anything(),
resultType: 'full',
resultFormat: 'xlsx',
}),
);
exploreUtils.exportChart.restore();
});
});
2 changes: 2 additions & 0 deletions superset-frontend/src/logger/LogUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export const LOG_ACTIONS_PERIODIC_RENDER_DASHBOARD =
export const LOG_ACTIONS_EXPLORE_DASHBOARD_CHART = 'explore_dashboard_chart';
export const LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART =
'export_csv_dashboard_chart';
export const LOG_ACTIONS_EXPORT_XLSX_DASHBOARD_CHART =
'export_csv_dashboard_chart';
export const LOG_ACTIONS_CHANGE_DASHBOARD_FILTER = 'change_dashboard_filter';
export const LOG_ACTIONS_DATASET_CREATION_EMPTY_CANCELLATION =
'dataset_creation_empty_cancellation';
Expand Down