mirror of https://github.com/apache/superset.git
chore: Improves the flow to create a new chart (#16252)
* chore: Improves the flow to create a new chart * Adds a comment about selector specificity
This commit is contained in:
parent
22231addec
commit
0668eaad6a
|
@ -18,15 +18,10 @@
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import rison from 'rison';
|
import rison from 'rison';
|
||||||
|
import { styled, t, SupersetClient, JsonResponse } from '@superset-ui/core';
|
||||||
|
import { Steps } from 'src/common/components';
|
||||||
import Button from 'src/components/Button';
|
import Button from 'src/components/Button';
|
||||||
import { Select } from 'src/components';
|
import { Select } from 'src/components';
|
||||||
import {
|
|
||||||
css,
|
|
||||||
styled,
|
|
||||||
t,
|
|
||||||
SupersetClient,
|
|
||||||
JsonResponse,
|
|
||||||
} from '@superset-ui/core';
|
|
||||||
import { FormLabel } from 'src/components/Form';
|
import { FormLabel } from 'src/components/Form';
|
||||||
import { Tooltip } from 'src/components/Tooltip';
|
import { Tooltip } from 'src/components/Tooltip';
|
||||||
|
|
||||||
|
@ -48,7 +43,8 @@ export type AddSliceContainerState = {
|
||||||
visType: string | null;
|
visType: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ESTIMATED_NAV_HEIGHT = '56px';
|
const ESTIMATED_NAV_HEIGHT = 56;
|
||||||
|
const ELEMENTS_EXCEPT_VIZ_GALLERY = ESTIMATED_NAV_HEIGHT + 250;
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
${({ theme }) => `
|
${({ theme }) => `
|
||||||
|
@ -58,7 +54,7 @@ const StyledContainer = styled.div`
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: ${MAX_ADVISABLE_VIZ_GALLERY_WIDTH}px;
|
max-width: ${MAX_ADVISABLE_VIZ_GALLERY_WIDTH}px;
|
||||||
max-height: calc(100vh - ${ESTIMATED_NAV_HEIGHT});
|
max-height: calc(100vh - ${ESTIMATED_NAV_HEIGHT}px);
|
||||||
border-radius: ${theme.gridUnit}px;
|
border-radius: ${theme.gridUnit}px;
|
||||||
background-color: ${theme.colors.grayscale.light5};
|
background-color: ${theme.colors.grayscale.light5};
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -75,6 +71,7 @@ const StyledContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-bottom: ${theme.gridUnit * 2}px;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
|
@ -84,11 +81,56 @@ const StyledContainer = styled.div`
|
||||||
& > span {
|
& > span {
|
||||||
color: ${theme.colors.grayscale.light1};
|
color: ${theme.colors.grayscale.light1};
|
||||||
margin-left: ${theme.gridUnit * 4}px;
|
margin-left: ${theme.gridUnit * 4}px;
|
||||||
margin-top: ${theme.gridUnit * 6}px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& .ant-tooltip-open {
|
& .viz-gallery {
|
||||||
|
border: 1px solid ${theme.colors.grayscale.light2};
|
||||||
|
border-radius: ${theme.gridUnit}px;
|
||||||
|
margin: ${theme.gridUnit}px 0px;
|
||||||
|
max-height: calc(100vh - ${ELEMENTS_EXCEPT_VIZ_GALLERY}px);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .footer {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
color: ${theme.colors.grayscale.light1};
|
||||||
|
margin-right: ${theme.gridUnit * 4}px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following extra ampersands (&&&&) are used to boost selector specificity */
|
||||||
|
|
||||||
|
&&&& .ant-steps-item-tail {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&&&& .ant-steps-item-icon {
|
||||||
|
margin-right: ${theme.gridUnit * 2}px;
|
||||||
|
width: ${theme.gridUnit * 5}px;
|
||||||
|
height: ${theme.gridUnit * 5}px;
|
||||||
|
line-height: ${theme.gridUnit * 5}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&&&& .ant-steps-item-title {
|
||||||
|
line-height: ${theme.gridUnit * 5}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&&&& .ant-steps-item-content {
|
||||||
|
overflow: unset;
|
||||||
|
|
||||||
|
.ant-steps-item-description {
|
||||||
|
margin-top: ${theme.gridUnit}px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&&&& .ant-tooltip-open {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,19 +168,6 @@ const TooltipContent = styled.div<{ hasDescription: boolean }>`
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const cssStatic = css`
|
|
||||||
flex: 0 0 auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledVizTypeGallery = styled(VizTypeGallery)`
|
|
||||||
${({ theme }) => `
|
|
||||||
border: 1px solid ${theme.colors.grayscale.light2};
|
|
||||||
border-radius: ${theme.gridUnit}px;
|
|
||||||
margin: ${theme.gridUnit * 3}px 0px;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledLabel = styled.span`
|
const StyledLabel = styled.span`
|
||||||
${({ theme }) => `
|
${({ theme }) => `
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -215,18 +244,27 @@ export default class AddSliceContainer extends React.PureComponent<
|
||||||
loadDatasources(search: string, page: number, pageSize: number) {
|
loadDatasources(search: string, page: number, pageSize: number) {
|
||||||
const query = rison.encode({
|
const query = rison.encode({
|
||||||
columns: ['id', 'table_name', 'description', 'datasource_type'],
|
columns: ['id', 'table_name', 'description', 'datasource_type'],
|
||||||
filter: search,
|
filters: [{ col: 'table_name', opr: 'ct', value: search }],
|
||||||
page,
|
page,
|
||||||
page_size: pageSize,
|
page_size: pageSize,
|
||||||
|
order_column: 'table_name',
|
||||||
|
order_direction: 'asc',
|
||||||
});
|
});
|
||||||
return SupersetClient.get({
|
return SupersetClient.get({
|
||||||
endpoint: `/api/v1/dataset?q=${query}`,
|
endpoint: `/api/v1/dataset/?q=${query}`,
|
||||||
}).then((response: JsonResponse) => {
|
}).then((response: JsonResponse) => {
|
||||||
const list = response.json.result.map((item: Dataset) => ({
|
const list: {
|
||||||
value: `${item.id}__${item.datasource_type}`,
|
label: string;
|
||||||
label: this.newLabel(item),
|
value: string;
|
||||||
labelText: item.table_name,
|
}[] = response.json.result
|
||||||
}));
|
.map((item: Dataset) => ({
|
||||||
|
value: `${item.id}__${item.datasource_type}`,
|
||||||
|
label: this.newLabel(item),
|
||||||
|
labelText: item.table_name,
|
||||||
|
}))
|
||||||
|
.sort((a: { labelText: string }, b: { labelText: string }) =>
|
||||||
|
a.labelText.localeCompare(b.labelText),
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
data: list,
|
data: list,
|
||||||
totalCount: response.json.count,
|
totalCount: response.json.count,
|
||||||
|
@ -244,52 +282,68 @@ export default class AddSliceContainer extends React.PureComponent<
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const isButtonDisabled = this.isBtnDisabled();
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<h3 css={cssStatic}>{t('Create a new chart')}</h3>
|
<h3>{t('Create a new chart')}</h3>
|
||||||
<div className="dataset">
|
<Steps direction="vertical" size="small">
|
||||||
<Select
|
<Steps.Step
|
||||||
autoFocus
|
title={<FormLabel>{t('Choose a dataset')}</FormLabel>}
|
||||||
ariaLabel={t('Dataset')}
|
status={this.state.datasource?.value ? 'finish' : 'process'}
|
||||||
name="select-datasource"
|
description={
|
||||||
header={<FormLabel required>{t('Choose a dataset')}</FormLabel>}
|
<div className="dataset">
|
||||||
filterOption={this.handleFilterOption}
|
<Select
|
||||||
onChange={this.changeDatasource}
|
autoFocus
|
||||||
options={this.loadDatasources}
|
ariaLabel={t('Dataset')}
|
||||||
placeholder={t('Choose a dataset')}
|
name="select-datasource"
|
||||||
showSearch
|
filterOption={this.handleFilterOption}
|
||||||
value={this.state.datasource}
|
onChange={this.changeDatasource}
|
||||||
|
options={this.loadDatasources}
|
||||||
|
placeholder={t('Choose a dataset')}
|
||||||
|
showSearch
|
||||||
|
value={this.state.datasource}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
{t(
|
||||||
|
'Instructions to add a dataset are available in the Superset tutorial.',
|
||||||
|
)}{' '}
|
||||||
|
<a
|
||||||
|
href="https://superset.apache.org/docs/creating-charts-dashboards/first-dashboard#adding-a-new-table"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<i className="fa fa-external-link" />
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<span>
|
<Steps.Step
|
||||||
{t(
|
title={<FormLabel>{t('Choose chart type')}</FormLabel>}
|
||||||
'Instructions to add a dataset are available in the Superset tutorial.',
|
status={this.state.visType ? 'finish' : 'process'}
|
||||||
)}{' '}
|
description={
|
||||||
<a
|
<VizTypeGallery
|
||||||
href="https://superset.apache.org/docs/creating-charts-dashboards/first-dashboard#adding-a-new-table"
|
className="viz-gallery"
|
||||||
rel="noopener noreferrer"
|
onChange={this.changeVisType}
|
||||||
target="_blank"
|
selectedViz={this.state.visType}
|
||||||
>
|
/>
|
||||||
<i className="fa fa-external-link" />
|
}
|
||||||
</a>
|
/>
|
||||||
</span>
|
</Steps>
|
||||||
|
<div className="footer">
|
||||||
|
{isButtonDisabled && (
|
||||||
|
<span>
|
||||||
|
{t('Please select both a Dataset and a Chart type to proceed')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
buttonStyle="primary"
|
||||||
|
disabled={isButtonDisabled}
|
||||||
|
onClick={this.gotoSlice}
|
||||||
|
>
|
||||||
|
{t('Create new chart')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<StyledVizTypeGallery
|
|
||||||
onChange={this.changeVisType}
|
|
||||||
selectedViz={this.state.visType}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
css={[
|
|
||||||
cssStatic,
|
|
||||||
css`
|
|
||||||
align-self: flex-end;
|
|
||||||
`,
|
|
||||||
]}
|
|
||||||
buttonStyle="primary"
|
|
||||||
disabled={this.isBtnDisabled()}
|
|
||||||
onClick={this.gotoSlice}
|
|
||||||
>
|
|
||||||
{t('Create new chart')}
|
|
||||||
</Button>
|
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ export {
|
||||||
Row,
|
Row,
|
||||||
Space,
|
Space,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
|
Steps,
|
||||||
Switch,
|
Switch,
|
||||||
Tag,
|
Tag,
|
||||||
Tabs,
|
Tabs,
|
||||||
|
|
|
@ -129,6 +129,11 @@ const StyledError = styled.div`
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledErrorMessage = styled.div`
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
`;
|
||||||
|
|
||||||
const StyledSpin = styled(Spin)`
|
const StyledSpin = styled(Spin)`
|
||||||
margin-top: ${({ theme }) => -theme.gridUnit}px;
|
margin-top: ${({ theme }) => -theme.gridUnit}px;
|
||||||
`;
|
`;
|
||||||
|
@ -141,7 +146,7 @@ const EMPTY_OPTIONS: OptionsType = [];
|
||||||
|
|
||||||
const Error = ({ error }: { error: string }) => (
|
const Error = ({ error }: { error: string }) => (
|
||||||
<StyledError>
|
<StyledError>
|
||||||
<Icons.ErrorSolid /> {error}
|
<Icons.ErrorSolid /> <StyledErrorMessage>{error}</StyledErrorMessage>
|
||||||
</StyledError>
|
</StyledError>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue