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: Resizable dataset and controls panels on Explore view #12411

Merged
merged 4 commits into from
Jan 12, 2021
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
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 @@ -150,6 +150,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 }}
Copy link
Member

Choose a reason for hiding this comment

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

We should make this width persistent and save it in localStorage. Could be in another PR, though.

minWidth={320}
maxWidth="33%"
Copy link
Member

@ktmud ktmud Jan 11, 2021

Choose a reason for hiding this comment

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

What do you think of increasing maxWidth to somewhere around 40% to 50%? Sometimes it's useful to test how the chart looks like when in very small containers.

Copy link
Member

@junlincc junlincc Jan 11, 2021

Choose a reason for hiding this comment

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

33% is already squeezing the viz. we tested different options. we probably don't want to increase the max width and compromise the chart area.

Copy link
Member Author

Choose a reason for hiding this comment

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

Increasing maxWidth wouldn't do much, as the chart section has a minWidth 512px set.

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