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:
Kamil Gabryjelski
2022-04-05 15:20:29 +02:00
committed by GitHub
parent 1eef923b31
commit 602afbaa31
8 changed files with 604 additions and 302 deletions

View File

@@ -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>
);
}