Skip to content

Commit

Permalink
feat: Resizable dataset and controls panels on Explore view (#12411)
Browse files Browse the repository at this point in the history
* Implement resizable panels on explore view

* Optimize chart rendering while resizing

* Make dataset column narrower

Co-authored-by: Evan Rusackas <evan@preset.io>
  • Loading branch information
kgabryje and rusackas authored Jan 12, 2021
1 parent d8f4443 commit ed53b00
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 57 deletions.
21 changes: 21 additions & 0 deletions superset-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions superset-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
"react-loadable": "^5.5.0",
"react-markdown": "^4.3.1",
"react-redux": "^7.2.0",
"react-resize-detector": "^6.0.1-rc.1",
"react-router-dom": "^5.1.2",
"react-search-input": "^0.11.3",
"react-select": "^3.1.0",
Expand Down
26 changes: 22 additions & 4 deletions superset-frontend/src/explore/components/DatasourcePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@ const DatasourceContainer = styled.div`
}
`;

const LabelContainer = styled.div`
overflow: hidden;
text-overflow: ellipsis;
& > span {
white-space: nowrap;
}
.option-label {
display: inline;
}
.metric-option > .option-label {
overflow: hidden;
text-overflow: ellipsis;
}
`;

const DataSourcePanel = ({
datasource,
controls: { datasource: datasourceControl },
Expand Down Expand Up @@ -200,9 +218,9 @@ const DataSourcePanel = ({
{t(`Showing %s of %s`, metricSlice.length, metrics.length)}
</div>
{metricSlice.map(m => (
<div key={m.metric_name} className="column">
<LabelContainer key={m.metric_name} className="column">
<MetricOption metric={m} showType />
</div>
</LabelContainer>
))}
</Collapse.Panel>
<Collapse.Panel
Expand All @@ -213,9 +231,9 @@ const DataSourcePanel = ({
{t(`Showing %s of %s`, columnSlice.length, columns.length)}
</div>
{columnSlice.map(col => (
<div key={col.column_name} className="column">
<LabelContainer key={col.column_name} className="column">
<ColumnOption column={col} showType />
</div>
</LabelContainer>
))}
</Collapse.Panel>
</Collapse>
Expand Down
104 changes: 57 additions & 47 deletions superset-frontend/src/explore/components/ExploreChartPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useState, useEffect, useRef, useCallback } from 'react';
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import Split from 'react-split';
import { ParentSize } from '@vx/responsive';
import { styled, useTheme } from '@superset-ui/core';
import debounce from 'lodash/debounce';
import { useResizeDetector } from 'react-resize-detector';
import { chartPropShape } from 'src/dashboard/util/propShapes';
import ChartContainer from 'src/chart/ChartContainer';
import ConnectedExploreChartHeader from './ExploreChartHeader';
Expand Down Expand Up @@ -55,6 +54,7 @@ const propTypes = {
const GUTTER_SIZE_FACTOR = 1.25;

const CHART_PANEL_PADDING = 30;
const HEADER_PADDING = 15;

const INITIAL_SIZES = [90, 10];
const MIN_SIZES = [300, 50];
Expand Down Expand Up @@ -104,20 +104,32 @@ const ExploreChartPanel = props => {
const gutterMargin = theme.gridUnit * GUTTER_SIZE_FACTOR;
const gutterHeight = theme.gridUnit * GUTTER_SIZE_FACTOR;

const panelHeadingRef = useRef(null);
const { height: hHeight, ref: headerRef } = useResizeDetector({
refreshMode: 'debounce',
refreshRate: 300,
});
const { width: chartWidth, ref: chartRef } = useResizeDetector({
refreshMode: 'debounce',
refreshRate: 300,
});
const [splitSizes, setSplitSizes] = useState(INITIAL_SIZES);

const calcSectionHeight = useCallback(
percent => {
const headerHeight = props.standalone
? 0
: panelHeadingRef?.current?.offsetHeight ?? 50;
let headerHeight;
if (props.standalone) {
headerHeight = 0;
} else if (hHeight) {
headerHeight = hHeight + HEADER_PADDING;
} else {
headerHeight = 50;
}
const containerHeight = parseInt(props.height, 10) - headerHeight;
return (
(containerHeight * percent) / 100 - (gutterHeight / 2 + gutterMargin)
);
},
[gutterHeight, gutterMargin, props.height, props.standalone],
[gutterHeight, gutterMargin, props.height, props.standalone, hHeight],
);

const [tableSectionHeight, setTableSectionHeight] = useState(
Expand All @@ -132,15 +144,11 @@ const ExploreChartPanel = props => {
);

useEffect(() => {
const recalcSizes = debounce(() => recalcPanelSizes(splitSizes), 200);

window.addEventListener('resize', recalcSizes);
return () => window.removeEventListener('resize', recalcSizes);
}, [props.standalone, recalcPanelSizes, splitSizes]);
recalcPanelSizes(splitSizes);
}, [recalcPanelSizes, splitSizes]);

const onDragEnd = sizes => {
setSplitSizes(sizes);
recalcPanelSizes(sizes);
};

const onCollapseChange = openPanelName => {
Expand All @@ -154,42 +162,46 @@ const ExploreChartPanel = props => {
];
}
setSplitSizes(splitSizes);
recalcPanelSizes(splitSizes);
};

const renderChart = () => {
const renderChart = useCallback(() => {
const { chart } = props;
const newHeight = calcSectionHeight(splitSizes[0]) - CHART_PANEL_PADDING;
return (
<ParentSize>
{({ width }) =>
width > 0 && (
<ChartContainer
width={Math.floor(width)}
height={newHeight}
annotationData={chart.annotationData}
chartAlert={chart.chartAlert}
chartStackTrace={chart.chartStackTrace}
chartId={chart.id}
chartStatus={chart.chartStatus}
triggerRender={props.triggerRender}
datasource={props.datasource}
errorMessage={props.errorMessage}
formData={props.form_data}
onQuery={props.onQuery}
owners={props?.slice?.owners}
queriesResponse={chart.queriesResponse}
refreshOverlayVisible={props.refreshOverlayVisible}
setControlValue={props.actions.setControlValue}
timeout={props.timeout}
triggerQuery={chart.triggerQuery}
vizType={props.vizType}
/>
)
}
</ParentSize>
chartWidth > 0 && (
<ChartContainer
width={Math.floor(chartWidth)}
height={newHeight}
annotationData={chart.annotationData}
chartAlert={chart.chartAlert}
chartStackTrace={chart.chartStackTrace}
chartId={chart.id}
chartStatus={chart.chartStatus}
triggerRender={props.triggerRender}
datasource={props.datasource}
errorMessage={props.errorMessage}
formData={props.form_data}
onQuery={props.onQuery}
owners={props?.slice?.owners}
queriesResponse={chart.queriesResponse}
refreshOverlayVisible={props.refreshOverlayVisible}
setControlValue={props.actions.setControlValue}
timeout={props.timeout}
triggerQuery={chart.triggerQuery}
vizType={props.vizType}
/>
)
);
};
}, [calcSectionHeight, chartWidth, props, splitSizes]);

const panelBody = useMemo(
() => (
<div className="panel-body" ref={chartRef}>
{renderChart()}
</div>
),
[chartRef, renderChart],
);

if (props.standalone) {
// dom manipulation hack to get rid of the boostrap theme's body background
Expand Down Expand Up @@ -222,14 +234,12 @@ const ExploreChartPanel = props => {
[dimension]: `calc(${elementSize}% - ${gutterSize + gutterMargin}px)`,
});

const panelBody = <div className="panel-body">{renderChart()}</div>;

return (
<Styles
className="panel panel-default chart-container"
style={{ height: props.height }}
>
<div className="panel-heading" ref={panelHeadingRef}>
<div className="panel-heading" ref={headerRef}>
{header}
</div>
{props.vizType === 'filter_box' ? (
Expand Down
21 changes: 15 additions & 6 deletions superset-frontend/src/explore/components/ExploreViewContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { styled, t, supersetTheme, css } from '@superset-ui/core';
import { debounce } from 'lodash';
import { Resizable } from 're-resizable';

import { useDynamicPluginContext } from 'src/components/DynamicPlugins';
import { Global } from '@emotion/core';
Expand Down Expand Up @@ -81,10 +82,8 @@ const Styles = styled.div`
border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
.explore-column {
display: flex;
flex: 0 0 ${({ theme }) => theme.gridUnit * 95}px;
flex-direction: column;
padding: ${({ theme }) => 2 * theme.gridUnit}px 0;
max-width: ${({ theme }) => theme.gridUnit * 95}px;
max-height: 100%;
}
.data-source-selection {
Expand Down Expand Up @@ -404,7 +403,11 @@ function ExploreViewContainer(props) {
dashboardId={props.dashboardId}
/>
)}
<div
<Resizable
defaultSize={{ width: 300 }}
minWidth={300}
maxWidth="33%"
enable={{ right: true }}
className={
isCollapsed ? 'no-show' : 'explore-column data-source-selection'
}
Expand All @@ -430,7 +433,7 @@ function ExploreViewContainer(props) {
controls={props.controls}
actions={props.actions}
/>
</div>
</Resizable>
{isCollapsed ? (
<div
className="sidebar"
Expand All @@ -452,7 +455,13 @@ function ExploreViewContainer(props) {
<Icon name="dataset-physical" width={16} />
</div>
) : null}
<div className="col-sm-3 explore-column controls-column">
<Resizable
defaultSize={{ width: 320 }}
minWidth={320}
maxWidth="33%"
enable={{ right: true }}
className="col-sm-3 explore-column controls-column"
>
<QueryAndSaveBtns
canAdd={!!(props.can_add || props.can_overwrite)}
onQuery={onQuery}
Expand All @@ -470,7 +479,7 @@ function ExploreViewContainer(props) {
datasource_type={props.datasource_type}
isDatasourceMetaLoading={props.isDatasourceMetaLoading}
/>
</div>
</Resizable>
<div
className={`main-explore-content ${
isCollapsed ? 'col-sm-9' : 'col-sm-7'
Expand Down

0 comments on commit ed53b00

Please sign in to comment.