Skip to content

Commit

Permalink
refactor: decouple DataTablesPane (#20109)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhaoyongjie authored May 26, 2022
1 parent 694f75d commit e9007e3
Show file tree
Hide file tree
Showing 18 changed files with 811 additions and 570 deletions.
15 changes: 14 additions & 1 deletion superset-frontend/src/components/Chart/chartAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,6 @@ export function exploreJSON(
return Promise.all([
chartDataRequestCaught,
dispatch(triggerQuery(false, key)),
dispatch(updateQueryFormData(formData, key)),
...annotationLayers.map(annotation =>
dispatch(
runAnnotationQuery({
Expand Down Expand Up @@ -595,3 +594,17 @@ export function refreshChart(chartKey, force, dashboardId) {
);
};
}

export const getDatasetSamples = async (datasetId, force) => {
const endpoint = `/api/v1/dataset/${datasetId}/samples?force=${force}`;
try {
const response = await SupersetClient.get({ endpoint });
return response.json.result;
} catch (err) {
const clientError = await getClientErrorObject(err);
throw new Error(
clientError.message || clientError.error || t('Sorry, an error occurred'),
{ cause: err },
);
}
};
43 changes: 16 additions & 27 deletions superset-frontend/src/components/Chart/chartActions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ describe('chart actions', () => {
const actionThunk = actions.postChartFormData({});

return actionThunk(dispatch).then(() => {
// chart update, trigger query, update form data, success
expect(dispatch.callCount).toBe(5);
// chart update, trigger query, success
expect(dispatch.callCount).toBe(4);
expect(fetchMock.calls(MOCK_URL)).toHaveLength(1);
expect(dispatch.args[0][0].type).toBe(actions.CHART_UPDATE_STARTED);
});
Expand All @@ -116,43 +116,32 @@ describe('chart actions', () => {
const actionThunk = actions.postChartFormData({});
return actionThunk(dispatch).then(() => {
// chart update, trigger query, update form data, success
expect(dispatch.callCount).toBe(5);
expect(dispatch.callCount).toBe(4);
expect(fetchMock.calls(MOCK_URL)).toHaveLength(1);
expect(dispatch.args[1][0].type).toBe(actions.TRIGGER_QUERY);
});
});

it('should dispatch UPDATE_QUERY_FORM_DATA action with the query', () => {
const actionThunk = actions.postChartFormData({});
return actionThunk(dispatch).then(() => {
// chart update, trigger query, update form data, success
expect(dispatch.callCount).toBe(5);
expect(fetchMock.calls(MOCK_URL)).toHaveLength(1);
expect(dispatch.args[2][0].type).toBe(actions.UPDATE_QUERY_FORM_DATA);
});
});

it('should dispatch logEvent async action', () => {
const actionThunk = actions.postChartFormData({});
return actionThunk(dispatch).then(() => {
// chart update, trigger query, update form data, success
expect(dispatch.callCount).toBe(5);
// chart update, trigger query, success
expect(dispatch.callCount).toBe(4);
expect(fetchMock.calls(MOCK_URL)).toHaveLength(1);
expect(typeof dispatch.args[3][0]).toBe('function');

dispatch.args[3][0](dispatch);
expect(dispatch.callCount).toBe(6);
expect(dispatch.args[5][0].type).toBe(LOG_EVENT);
dispatch.args[2][0](dispatch);
expect(dispatch.callCount).toBe(5);
expect(dispatch.args[4][0].type).toBe(LOG_EVENT);
});
});

it('should dispatch CHART_UPDATE_SUCCEEDED action upon success', () => {
const actionThunk = actions.postChartFormData({});
return actionThunk(dispatch).then(() => {
// chart update, trigger query, update form data, success
expect(dispatch.callCount).toBe(5);
// chart update, trigger query, success
expect(dispatch.callCount).toBe(4);
expect(fetchMock.calls(MOCK_URL)).toHaveLength(1);
expect(dispatch.args[4][0].type).toBe(actions.CHART_UPDATE_SUCCEEDED);
expect(dispatch.args[3][0].type).toBe(actions.CHART_UPDATE_SUCCEEDED);
});
});

Expand All @@ -168,8 +157,8 @@ describe('chart actions', () => {
return actionThunk(dispatch).then(() => {
// chart update, trigger query, update form data, fail
expect(fetchMock.calls(MOCK_URL)).toHaveLength(1);
expect(dispatch.callCount).toBe(5);
expect(dispatch.args[4][0].type).toBe(actions.CHART_UPDATE_FAILED);
expect(dispatch.callCount).toBe(4);
expect(dispatch.args[3][0].type).toBe(actions.CHART_UPDATE_FAILED);
setupDefaultFetchMock();
});
});
Expand All @@ -185,9 +174,9 @@ describe('chart actions', () => {
const actionThunk = actions.postChartFormData({}, false, timeoutInSec);

return actionThunk(dispatch).then(() => {
// chart update, trigger query, update form data, fail
expect(dispatch.callCount).toBe(5);
const updateFailedAction = dispatch.args[4][0];
// chart update, trigger query, fail
expect(dispatch.callCount).toBe(4);
const updateFailedAction = dispatch.args[3][0];
expect(updateFailedAction.type).toBe(actions.CHART_UPDATE_FAILED);
expect(updateFailedAction.queriesResponse[0].error).toBe('misc error');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('Dashboard', () => {
removeSliceFromDashboard() {},
triggerQuery() {},
logEvent() {},
updateQueryFormData() {},
},
initMessages: [],
dashboardState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
screen,
waitForElementToBeRemoved,
} from 'spec/helpers/testing-library';
import { DatasourceType } from '@superset-ui/core';
import { exploreActions } from 'src/explore/actions/exploreActions';
import { DataTablesPane } from '.';

const createProps = () => ({
Expand Down Expand Up @@ -62,6 +64,16 @@ const createProps = () => ({
colnames: [],
},
],
datasource: {
id: 0,
name: '',
type: DatasourceType.Table,
columns: [],
metrics: [],
columnFormats: {},
verboseMap: {},
},
actions: exploreActions,
});

describe('DataTablesPane', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/**
* 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 React, {
useCallback,
useEffect,
useMemo,
useState,
MouseEvent,
} from 'react';
import { styled, t, useTheme } from '@superset-ui/core';
import Icons from 'src/components/Icons';
import Tabs from 'src/components/Tabs';
import {
getItem,
setItem,
LocalStorageKeys,
} from 'src/utils/localStorageHelpers';
import { ResultsPane, SamplesPane, TableControlsWrapper } from './components';
import { DataTablesPaneProps } from './types';

enum ResultTypes {
Results = 'results',
Samples = 'samples',
}

const SouthPane = styled.div`
${({ theme }) => `
position: relative;
background-color: ${theme.colors.grayscale.light5};
z-index: 5;
overflow: hidden;
.ant-tabs {
height: 100%;
}
.ant-tabs-content-holder {
height: 100%;
}
.ant-tabs-content {
height: 100%;
}
.ant-tabs-tabpane {
display: flex;
flex-direction: column;
height: 100%;
.table-condensed {
height: 100%;
overflow: auto;
margin-bottom: ${theme.gridUnit * 4}px;
.table {
margin-bottom: ${theme.gridUnit * 2}px;
}
}
.pagination-container > ul[role='navigation'] {
margin-top: 0;
}
}
`}
`;

export const DataTablesPane = ({
queryFormData,
datasource,
queryForce,
onCollapseChange,
ownState,
errorMessage,
actions,
}: DataTablesPaneProps) => {
const theme = useTheme();
const [activeTabKey, setActiveTabKey] = useState<string>(ResultTypes.Results);
const [isRequest, setIsRequest] = useState<Record<ResultTypes, boolean>>({
results: getItem(LocalStorageKeys.is_datapanel_open, false),
samples: false,
});
const [panelOpen, setPanelOpen] = useState(
getItem(LocalStorageKeys.is_datapanel_open, false),
);

useEffect(() => {
setItem(LocalStorageKeys.is_datapanel_open, panelOpen);
}, [panelOpen]);

useEffect(() => {
if (!panelOpen) {
setIsRequest({
results: false,
samples: false,
});
}

if (panelOpen && activeTabKey === ResultTypes.Results) {
setIsRequest({
results: true,
samples: false,
});
}

if (panelOpen && activeTabKey === ResultTypes.Samples) {
setIsRequest({
results: false,
samples: true,
});
}
}, [panelOpen, activeTabKey]);

const handleCollapseChange = useCallback(
(isOpen: boolean) => {
onCollapseChange(isOpen);
setPanelOpen(isOpen);
},
[onCollapseChange],
);

const handleTabClick = useCallback(
(tabKey: string, e: MouseEvent) => {
if (!panelOpen) {
handleCollapseChange(true);
} else if (tabKey === activeTabKey) {
e.preventDefault();
handleCollapseChange(false);
}
setActiveTabKey(tabKey);
},
[activeTabKey, handleCollapseChange, panelOpen],
);

const CollapseButton = useMemo(() => {
const caretIcon = panelOpen ? (
<Icons.CaretUp
iconColor={theme.colors.grayscale.base}
aria-label={t('Collapse data panel')}
/>
) : (
<Icons.CaretDown
iconColor={theme.colors.grayscale.base}
aria-label={t('Expand data panel')}
/>
);
return (
<TableControlsWrapper>
{panelOpen ? (
<span
role="button"
tabIndex={0}
onClick={() => handleCollapseChange(false)}
>
{caretIcon}
</span>
) : (
<span
role="button"
tabIndex={0}
onClick={() => handleCollapseChange(true)}
>
{caretIcon}
</span>
)}
</TableControlsWrapper>
);
}, [handleCollapseChange, panelOpen, theme.colors.grayscale.base]);

return (
<SouthPane data-test="some-purposeful-instance">
<Tabs
fullWidth={false}
tabBarExtraContent={CollapseButton}
activeKey={panelOpen ? activeTabKey : ''}
onTabClick={handleTabClick}
>
<Tabs.TabPane tab={t('Results')} key={ResultTypes.Results}>
<ResultsPane
errorMessage={errorMessage}
queryFormData={queryFormData}
queryForce={queryForce}
ownState={ownState}
isRequest={isRequest.results}
actions={actions}
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('Samples')} key={ResultTypes.Samples}>
<SamplesPane
datasource={datasource}
queryForce={queryForce}
isRequest={isRequest.samples}
actions={actions}
/>
</Tabs.TabPane>
</Tabs>
</SouthPane>
);
};
Loading

0 comments on commit e9007e3

Please sign in to comment.