mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
feat(explore): Move chart header to top of the page (#19529)
* Move chart header to top of the page * Implement truncating and dynamic input * fix typing * Prevent cmd+z undoing changes when not in edit mode * Fix tests, add missing types * Show changed title in altered
This commit is contained in:
committed by
GitHub
parent
1eef923b31
commit
602afbaa31
@@ -60,6 +60,7 @@ import {
|
||||
LOG_ACTIONS_MOUNT_EXPLORER,
|
||||
LOG_ACTIONS_CHANGE_EXPLORE_CONTROLS,
|
||||
} from '../../../logger/LogUtils';
|
||||
import ConnectedExploreChartHeader from '../ExploreChartHeader';
|
||||
|
||||
const propTypes = {
|
||||
...ExploreChartPanel.propTypes,
|
||||
@@ -82,69 +83,96 @@ const propTypes = {
|
||||
vizType: PropTypes.string,
|
||||
};
|
||||
|
||||
const Styles = styled.div`
|
||||
background: ${({ theme }) => theme.colors.grayscale.light5};
|
||||
text-align: left;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
const ExploreContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
flex-basis: 100vh;
|
||||
align-items: stretch;
|
||||
border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
|
||||
.explore-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => 2 * theme.gridUnit}px 0;
|
||||
max-height: 100%;
|
||||
}
|
||||
.data-source-selection {
|
||||
background-color: ${({ theme }) => theme.colors.grayscale.light5};
|
||||
padding: ${({ theme }) => 2 * theme.gridUnit}px 0;
|
||||
border-right: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
|
||||
}
|
||||
.main-explore-content {
|
||||
flex: 1;
|
||||
min-width: ${({ theme }) => theme.gridUnit * 128}px;
|
||||
border-left: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
|
||||
.panel {
|
||||
margin-bottom: 0;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const ExploreHeaderContainer = styled.div`
|
||||
${({ theme }) => css`
|
||||
background-color: ${theme.colors.grayscale.light5};
|
||||
height: ${theme.gridUnit * 16}px;
|
||||
padding: 0 ${theme.gridUnit * 4}px;
|
||||
|
||||
.editable-title {
|
||||
overflow: hidden;
|
||||
|
||||
& > input[type='button'],
|
||||
& > span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
.controls-column {
|
||||
align-self: flex-start;
|
||||
padding: 0;
|
||||
}
|
||||
.title-container {
|
||||
`}
|
||||
`;
|
||||
|
||||
const ExplorePanelContainer = styled.div`
|
||||
${({ theme }) => css`
|
||||
background: ${theme.colors.grayscale.light5};
|
||||
text-align: left;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 ${({ theme }) => 2 * theme.gridUnit}px;
|
||||
justify-content: space-between;
|
||||
.horizontal-text {
|
||||
text-transform: uppercase;
|
||||
color: ${({ theme }) => theme.colors.grayscale.light1};
|
||||
font-size: ${({ theme }) => 4 * theme.typography.sizes.s};
|
||||
flex: 1;
|
||||
flex-wrap: nowrap;
|
||||
border-top: 1px solid ${theme.colors.grayscale.light2};
|
||||
.explore-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${theme.gridUnit * 2}px 0;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
.no-show {
|
||||
display: none;
|
||||
}
|
||||
.vertical-text {
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: mixed;
|
||||
}
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
background-color: ${({ theme }) => theme.colors.grayscale.light4};
|
||||
padding: ${({ theme }) => 2 * theme.gridUnit}px;
|
||||
width: ${({ theme }) => 8 * theme.gridUnit}px;
|
||||
}
|
||||
.callpase-icon > svg {
|
||||
color: ${({ theme }) => theme.colors.primary.base};
|
||||
}
|
||||
.data-source-selection {
|
||||
background-color: ${theme.colors.grayscale.light5};
|
||||
padding: ${theme.gridUnit * 2}px 0;
|
||||
border-right: 1px solid ${theme.colors.grayscale.light2};
|
||||
}
|
||||
.main-explore-content {
|
||||
flex: 1;
|
||||
min-width: ${theme.gridUnit * 128}px;
|
||||
border-left: 1px solid ${theme.colors.grayscale.light2};
|
||||
.panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.controls-column {
|
||||
align-self: flex-start;
|
||||
padding: 0;
|
||||
}
|
||||
.title-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 ${theme.gridUnit * 4}px;
|
||||
justify-content: space-between;
|
||||
.horizontal-text {
|
||||
text-transform: uppercase;
|
||||
color: ${theme.colors.grayscale.light1};
|
||||
font-size: ${theme.typography.sizes.s * 4};
|
||||
}
|
||||
}
|
||||
.no-show {
|
||||
display: none;
|
||||
}
|
||||
.vertical-text {
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: mixed;
|
||||
}
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
background-color: ${theme.colors.grayscale.light4};
|
||||
padding: ${theme.gridUnit * 2}px;
|
||||
width: ${theme.gridUnit * 8}px;
|
||||
}
|
||||
.callpase-icon > svg {
|
||||
color: ${theme.colors.primary.base};
|
||||
}
|
||||
`};
|
||||
`;
|
||||
|
||||
const getWindowSize = () => ({
|
||||
@@ -230,7 +258,7 @@ function ExploreViewContainer(props) {
|
||||
|
||||
const theme = useTheme();
|
||||
const width = `${windowSize.width}px`;
|
||||
const navHeight = props.standalone ? 0 : 90;
|
||||
const navHeight = props.standalone ? 0 : 120;
|
||||
const height = props.forcedHeight
|
||||
? `${props.forcedHeight}px`
|
||||
: `${windowSize.height - navHeight}px`;
|
||||
@@ -515,144 +543,164 @@ function ExploreViewContainer(props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Styles id="explore-container" height={height}>
|
||||
<Global
|
||||
styles={css`
|
||||
.navbar {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
body {
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
#app-menu,
|
||||
#app {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
#app {
|
||||
flex-basis: 100%;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
#app-menu {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
{showingModal && (
|
||||
<SaveModal
|
||||
onHide={toggleModal}
|
||||
<ExploreContainer>
|
||||
<ExploreHeaderContainer>
|
||||
<ConnectedExploreChartHeader
|
||||
ownState={props.ownState}
|
||||
actions={props.actions}
|
||||
form_data={props.form_data}
|
||||
sliceName={props.sliceName}
|
||||
canOverwrite={props.can_overwrite}
|
||||
canDownload={props.can_download}
|
||||
dashboardId={props.dashboardId}
|
||||
isStarred={props.isStarred}
|
||||
slice={props.slice}
|
||||
sliceName={props.sliceName}
|
||||
table_name={props.table_name}
|
||||
formData={props.form_data}
|
||||
timeout={props.timeout}
|
||||
chart={props.chart}
|
||||
user={props.user}
|
||||
reports={props.reports}
|
||||
/>
|
||||
)}
|
||||
<Resizable
|
||||
onResizeStop={(evt, direction, ref, d) => {
|
||||
setShouldForceUpdate(d?.width);
|
||||
setSidebarWidths(LocalStorageKeys.datasource_width, d);
|
||||
}}
|
||||
defaultSize={{
|
||||
width: getSidebarWidths(LocalStorageKeys.datasource_width),
|
||||
height: '100%',
|
||||
}}
|
||||
minWidth={defaultSidebarsWidth[LocalStorageKeys.datasource_width]}
|
||||
maxWidth="33%"
|
||||
enable={{ right: true }}
|
||||
className={
|
||||
isCollapsed ? 'no-show' : 'explore-column data-source-selection'
|
||||
}
|
||||
>
|
||||
<div className="title-container">
|
||||
<span className="horizont al-text">{t('Dataset')}</span>
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="action-button"
|
||||
onClick={toggleCollapse}
|
||||
>
|
||||
<Icons.Expand
|
||||
className="collapse-icon"
|
||||
iconColor={theme.colors.primary.base}
|
||||
iconSize="l"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<DataSourcePanel
|
||||
datasource={props.datasource}
|
||||
controls={props.controls}
|
||||
actions={props.actions}
|
||||
shouldForceUpdate={shouldForceUpdate}
|
||||
</ExploreHeaderContainer>
|
||||
<ExplorePanelContainer id="explore-container">
|
||||
<Global
|
||||
styles={css`
|
||||
.navbar {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
body {
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
#app-menu,
|
||||
#app {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
#app {
|
||||
flex-basis: 100%;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
#app-menu {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
</Resizable>
|
||||
{isCollapsed ? (
|
||||
<div
|
||||
className="sidebar"
|
||||
onClick={toggleCollapse}
|
||||
data-test="open-datasource-tab"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
{showingModal && (
|
||||
<SaveModal
|
||||
onHide={toggleModal}
|
||||
actions={props.actions}
|
||||
form_data={props.form_data}
|
||||
sliceName={props.sliceName}
|
||||
dashboardId={props.dashboardId}
|
||||
/>
|
||||
)}
|
||||
<Resizable
|
||||
onResizeStop={(evt, direction, ref, d) => {
|
||||
setShouldForceUpdate(d?.width);
|
||||
setSidebarWidths(LocalStorageKeys.datasource_width, d);
|
||||
}}
|
||||
defaultSize={{
|
||||
width: getSidebarWidths(LocalStorageKeys.datasource_width),
|
||||
height: '100%',
|
||||
}}
|
||||
minWidth={defaultSidebarsWidth[LocalStorageKeys.datasource_width]}
|
||||
maxWidth="33%"
|
||||
enable={{ right: true }}
|
||||
className={
|
||||
isCollapsed ? 'no-show' : 'explore-column data-source-selection'
|
||||
}
|
||||
>
|
||||
<span role="button" tabIndex={0} className="action-button">
|
||||
<Tooltip title={t('Open Datasource tab')}>
|
||||
<Icons.Collapse
|
||||
<div className="title-container">
|
||||
<span className="horizont al-text">{t('Dataset')}</span>
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="action-button"
|
||||
onClick={toggleCollapse}
|
||||
>
|
||||
<Icons.Expand
|
||||
className="collapse-icon"
|
||||
iconColor={theme.colors.primary.base}
|
||||
iconSize="l"
|
||||
/>
|
||||
</Tooltip>
|
||||
</span>
|
||||
<Icons.DatasetPhysical
|
||||
css={{ marginTop: theme.gridUnit * 2 }}
|
||||
iconSize="l"
|
||||
iconColor={theme.colors.grayscale.base}
|
||||
</span>
|
||||
</div>
|
||||
<DataSourcePanel
|
||||
datasource={props.datasource}
|
||||
controls={props.controls}
|
||||
actions={props.actions}
|
||||
shouldForceUpdate={shouldForceUpdate}
|
||||
/>
|
||||
</Resizable>
|
||||
{isCollapsed ? (
|
||||
<div
|
||||
className="sidebar"
|
||||
onClick={toggleCollapse}
|
||||
data-test="open-datasource-tab"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<span role="button" tabIndex={0} className="action-button">
|
||||
<Tooltip title={t('Open Datasource tab')}>
|
||||
<Icons.Collapse
|
||||
className="collapse-icon"
|
||||
iconColor={theme.colors.primary.base}
|
||||
iconSize="l"
|
||||
/>
|
||||
</Tooltip>
|
||||
</span>
|
||||
<Icons.DatasetPhysical
|
||||
css={{ marginTop: theme.gridUnit * 2 }}
|
||||
iconSize="l"
|
||||
iconColor={theme.colors.grayscale.base}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<Resizable
|
||||
onResizeStop={(evt, direction, ref, d) =>
|
||||
setSidebarWidths(LocalStorageKeys.controls_width, d)
|
||||
}
|
||||
defaultSize={{
|
||||
width: getSidebarWidths(LocalStorageKeys.controls_width),
|
||||
height: '100%',
|
||||
}}
|
||||
minWidth={defaultSidebarsWidth[LocalStorageKeys.controls_width]}
|
||||
maxWidth="33%"
|
||||
enable={{ right: true }}
|
||||
className="col-sm-3 explore-column controls-column"
|
||||
>
|
||||
<QueryAndSaveBtns
|
||||
canAdd={!!(props.can_add || props.can_overwrite)}
|
||||
onQuery={onQuery}
|
||||
onSave={toggleModal}
|
||||
onStop={onStop}
|
||||
loading={props.chart.chartStatus === 'loading'}
|
||||
chartIsStale={chartIsStale}
|
||||
errorMessage={renderErrorMessage()}
|
||||
datasourceType={props.datasource_type}
|
||||
/>
|
||||
<ConnectedControlPanelsContainer
|
||||
exploreState={props.exploreState}
|
||||
actions={props.actions}
|
||||
form_data={props.form_data}
|
||||
controls={props.controls}
|
||||
chart={props.chart}
|
||||
datasource_type={props.datasource_type}
|
||||
isDatasourceMetaLoading={props.isDatasourceMetaLoading}
|
||||
/>
|
||||
</Resizable>
|
||||
<div
|
||||
className={cx(
|
||||
'main-explore-content',
|
||||
isCollapsed ? 'col-sm-9' : 'col-sm-7',
|
||||
)}
|
||||
>
|
||||
{renderChartContainer()}
|
||||
</div>
|
||||
) : null}
|
||||
<Resizable
|
||||
onResizeStop={(evt, direction, ref, d) =>
|
||||
setSidebarWidths(LocalStorageKeys.controls_width, d)
|
||||
}
|
||||
defaultSize={{
|
||||
width: getSidebarWidths(LocalStorageKeys.controls_width),
|
||||
height: '100%',
|
||||
}}
|
||||
minWidth={defaultSidebarsWidth[LocalStorageKeys.controls_width]}
|
||||
maxWidth="33%"
|
||||
enable={{ right: true }}
|
||||
className="col-sm-3 explore-column controls-column"
|
||||
>
|
||||
<QueryAndSaveBtns
|
||||
canAdd={!!(props.can_add || props.can_overwrite)}
|
||||
onQuery={onQuery}
|
||||
onSave={toggleModal}
|
||||
onStop={onStop}
|
||||
loading={props.chart.chartStatus === 'loading'}
|
||||
chartIsStale={chartIsStale}
|
||||
errorMessage={renderErrorMessage()}
|
||||
datasourceType={props.datasource_type}
|
||||
/>
|
||||
<ConnectedControlPanelsContainer
|
||||
exploreState={props.exploreState}
|
||||
actions={props.actions}
|
||||
form_data={props.form_data}
|
||||
controls={props.controls}
|
||||
chart={props.chart}
|
||||
datasource_type={props.datasource_type}
|
||||
isDatasourceMetaLoading={props.isDatasourceMetaLoading}
|
||||
/>
|
||||
</Resizable>
|
||||
<div
|
||||
className={cx(
|
||||
'main-explore-content',
|
||||
isCollapsed ? 'col-sm-9' : 'col-sm-7',
|
||||
)}
|
||||
>
|
||||
{renderChartContainer()}
|
||||
</div>
|
||||
</Styles>
|
||||
</ExplorePanelContainer>
|
||||
</ExploreContainer>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user