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: Drill by open in Explore #23575

Merged
merged 3 commits into from
Apr 5, 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 @@ -27,14 +27,17 @@ import {
} from './Operator';
import { TimeGranularity } from '../../time-format';

interface BaseSimpleAdhocFilter {
expressionType: 'SIMPLE';
interface BaseAdhocFilter {
clause: 'WHERE' | 'HAVING';
subject: string;
timeGrain?: TimeGranularity;
isExtra?: boolean;
}

interface BaseSimpleAdhocFilter extends BaseAdhocFilter {
expressionType: 'SIMPLE';
subject: string;
}

export type UnaryAdhocFilter = BaseSimpleAdhocFilter & {
operator: UnaryOperator;
};
Expand All @@ -54,9 +57,8 @@ export type SimpleAdhocFilter =
| BinaryAdhocFilter
| SetAdhocFilter;

export interface FreeFormAdhocFilter {
export interface FreeFormAdhocFilter extends BaseAdhocFilter {
expressionType: 'SQL';
clause: 'WHERE' | 'HAVING';
sqlExpression: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,30 +40,10 @@ const fetchWithNoData = () => {
});
};

const setup = (overrides: Record<string, any> = {}) => {
const props = {
column: { column_name: 'state' },
formData: { ...chart.form_data, viz_type: 'pie' },
groupbyFieldName: 'groupby',
...overrides,
};
return render(
<DrillByChart
filters={[
{
col: 'gender',
op: '==',
val: 'boy',
formattedVal: 'boy',
},
]}
{...props}
/>,
{
useRedux: true,
},
);
};
const setup = (overrides: Record<string, any> = {}) =>
render(<DrillByChart formData={{ ...chart.form_data, ...overrides }} />, {
useRedux: true,
});

const waitForRender = (overrides: Record<string, any> = {}) =>
waitFor(() => setup(overrides));
Expand Down
42 changes: 5 additions & 37 deletions superset-frontend/src/components/Chart/DrillBy/DrillByChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,20 @@
* under the License.
*/
import React, { useEffect, useState } from 'react';
import {
Behavior,
BinaryQueryObjectFilterClause,
Column,
css,
SuperChart,
} from '@superset-ui/core';
import { simpleFilterToAdhoc } from 'src/utils/simpleFilterToAdhoc';
import { BaseFormData, Behavior, css, SuperChart } from '@superset-ui/core';
import { getChartDataRequest } from 'src/components/Chart/chartAction';
import Loading from 'src/components/Loading';

interface DrillByChartProps {
column?: Column;
filters?: BinaryQueryObjectFilterClause[];
formData: { [key: string]: any; viz_type: string };
groupbyFieldName?: string;
formData: BaseFormData & { [key: string]: any };
}

export default function DrillByChart({
column,
filters,
formData,
groupbyFieldName = 'groupby',
}: DrillByChartProps) {
let updatedFormData = formData;
let groupbyField: any = [];
export default function DrillByChart({ formData }: DrillByChartProps) {
const [chartDataResult, setChartDataResult] = useState();

if (column) {
groupbyField = Array.isArray(formData[groupbyFieldName])
? [column.column_name]
: column.column_name;
}

if (filters) {
const adhocFilters = filters.map(filter => simpleFilterToAdhoc(filter));
updatedFormData = {
...formData,
adhoc_filters: [...formData.adhoc_filters, ...adhocFilters],
[groupbyFieldName]: groupbyField,
};
}

useEffect(() => {
getChartDataRequest({
formData: updatedFormData,
formData,
}).then(({ json }) => {
setChartDataResult(json.result);
});
Expand All @@ -81,7 +49,7 @@ export default function DrillByChart({
behaviors={[Behavior.INTERACTIVE_CHART]}
chartType={formData.viz_type}
enableNoResults
formData={updatedFormData}
formData={formData}
queriesData={chartDataResult}
height="100%"
width="100%"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,35 @@
*/

import React, { useState } from 'react';
import fetchMock from 'fetch-mock';
import { omit, isUndefined, omitBy } from 'lodash';
import userEvent from '@testing-library/user-event';
import { waitFor } from '@testing-library/react';
import { render, screen } from 'spec/helpers/testing-library';
import chartQueries, { sliceId } from 'spec/fixtures/mockChartQueries';
import mockState from 'spec/fixtures/mockState';
import fetchMock from 'fetch-mock';
import { DashboardPageIdContext } from 'src/dashboard/containers/DashboardPage';
import DrillByModal from './DrillByModal';

const CHART_DATA_ENDPOINT =
'glob:*api/v1/chart/data?form_data=%7B%22slice_id%22%3A18%7D';

fetchMock.post(CHART_DATA_ENDPOINT, { body: {} }, {});
const CHART_DATA_ENDPOINT = 'glob:*/api/v1/chart/data*';
const FORM_DATA_KEY_ENDPOINT = 'glob:*/api/v1/explore/form_data';

const { form_data: formData } = chartQueries[sliceId];
const { slice_name: chartName } = formData;
const drillByModalState = {
...mockState,
dashboardLayout: {
CHART_ID: {
id: 'CHART_ID',
meta: {
chartId: formData.slice_id,
sliceName: chartName,
past: [],
present: {
CHART_ID: {
id: 'CHART_ID',
meta: {
chartId: formData.slice_id,
sliceName: chartName,
},
},
},
future: [],
},
};
const dataset = {
Expand All @@ -56,12 +61,13 @@ const dataset = {
},
],
};
const renderModal = async (state?: object) => {

const renderModal = async () => {
const DrillByModalWrapper = () => {
const [showModal, setShowModal] = useState(false);

return (
<>
<DashboardPageIdContext.Provider value="1">
<button type="button" onClick={() => setShowModal(true)}>
Show modal
</button>
Expand All @@ -71,23 +77,29 @@ const renderModal = async (state?: object) => {
onHideModal={() => setShowModal(false)}
dataset={dataset}
/>
</>
</DashboardPageIdContext.Provider>
);
};
render(<DrillByModalWrapper />, {
useDnd: true,
useRedux: true,
useRouter: true,
initialState: state,
initialState: drillByModalState,
});

userEvent.click(screen.getByRole('button', { name: 'Show modal' }));
await screen.findByRole('dialog', { name: `Drill by: ${chartName}` });
};

beforeEach(() => {
fetchMock
.post(CHART_DATA_ENDPOINT, { body: {} }, {})
.post(FORM_DATA_KEY_ENDPOINT, { key: '123' });
});
afterEach(fetchMock.restore);

test('should render the title', async () => {
await renderModal(drillByModalState);
await renderModal();
expect(screen.getByText(`Drill by: ${chartName}`)).toBeInTheDocument();
});

Expand All @@ -105,3 +117,30 @@ test('should close the modal', async () => {
userEvent.click(screen.getAllByRole('button', { name: 'Close' })[1]);
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});

test('should generate Explore url', async () => {
await renderModal();
await waitFor(() => fetchMock.called(FORM_DATA_KEY_ENDPOINT));
const expectedRequestPayload = {
form_data: {
...omitBy(
omit(formData, ['slice_id', 'slice_name', 'dashboards']),
isUndefined,
),
slice_id: 0,
},
datasource_id: Number(formData.datasource.split('__')[0]),
datasource_type: formData.datasource.split('__')[1],
};

const parsedRequestPayload = JSON.parse(
fetchMock.lastCall()?.[1]?.body as string,
);
parsedRequestPayload.form_data = JSON.parse(parsedRequestPayload.form_data);

expect(parsedRequestPayload).toEqual(expectedRequestPayload);

expect(
await screen.findByRole('link', { name: 'Edit chart' }),
).toHaveAttribute('href', '/explore/?form_data_key=123&dashboard_page_id=1');
});
Loading