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:
Michael S. Molina 2021-08-16 11:56:47 -03:00 committed by GitHub
parent 22231addec
commit 0668eaad6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 74 deletions

View File

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

View File

@ -49,6 +49,7 @@ export {
Row, Row,
Space, Space,
Skeleton, Skeleton,
Steps,
Switch, Switch,
Tag, Tag,
Tabs, Tabs,

View File

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