diff --git a/superset-frontend/src/addSlice/AddSliceContainer.test.tsx b/superset-frontend/src/addSlice/AddSliceContainer.test.tsx index 2174a6b758ab9..00e7276a5864c 100644 --- a/superset-frontend/src/addSlice/AddSliceContainer.test.tsx +++ b/superset-frontend/src/addSlice/AddSliceContainer.test.tsx @@ -28,11 +28,9 @@ import VizTypeGallery from 'src/explore/components/controls/VizTypeControl/VizTy import { styledMount as mount } from 'spec/helpers/theming'; import { act } from 'spec/helpers/testing-library'; -const defaultProps = { - datasources: [ - { label: 'my first table', value: '1__table' }, - { label: 'another great table', value: '2__table' }, - ], +const datasource = { + value: '1', + label: 'table', }; describe('AddSliceContainer', () => { @@ -43,7 +41,7 @@ describe('AddSliceContainer', () => { >; beforeEach(async () => { - wrapper = mount() as ReactWrapper< + wrapper = mount() as ReactWrapper< AddSliceContainerProps, AddSliceContainerState, AddSliceContainer @@ -68,11 +66,8 @@ describe('AddSliceContainer', () => { }); it('renders an enabled button if datasource and viz type is selected', () => { - const datasourceValue = defaultProps.datasources[0].value; wrapper.setState({ - datasourceValue, - datasourceId: datasourceValue.split('__')[0], - datasourceType: datasourceValue.split('__')[1], + datasource, visType: 'table', }); expect( @@ -81,15 +76,12 @@ describe('AddSliceContainer', () => { }); it('formats explore url', () => { - const datasourceValue = defaultProps.datasources[0].value; wrapper.setState({ - datasourceValue, - datasourceId: datasourceValue.split('__')[0], - datasourceType: datasourceValue.split('__')[1], + datasource, visType: 'table', }); const formattedUrl = - '/superset/explore/?form_data=%7B%22viz_type%22%3A%22table%22%2C%22datasource%22%3A%221__table%22%7D'; + '/superset/explore/?form_data=%7B%22viz_type%22%3A%22table%22%2C%22datasource%22%3A%221%22%7D'; expect(wrapper.instance().exploreUrl()).toBe(formattedUrl); }); }); diff --git a/superset-frontend/src/addSlice/AddSliceContainer.tsx b/superset-frontend/src/addSlice/AddSliceContainer.tsx index b06ceef2df69e..0198ab01c8729 100644 --- a/superset-frontend/src/addSlice/AddSliceContainer.tsx +++ b/superset-frontend/src/addSlice/AddSliceContainer.tsx @@ -17,28 +17,34 @@ * under the License. */ import React from 'react'; +import rison from 'rison'; import Button from 'src/components/Button'; import { Select } from 'src/components'; -import { css, styled, t } from '@superset-ui/core'; +import { + css, + styled, + t, + SupersetClient, + JsonResponse, +} from '@superset-ui/core'; import { FormLabel } from 'src/components/Form'; +import { Tooltip } from 'src/components/Tooltip'; import VizTypeGallery, { MAX_ADVISABLE_VIZ_GALLERY_WIDTH, } from 'src/explore/components/controls/VizTypeControl/VizTypeGallery'; -interface Datasource { - label: string; - value: string; -} - -export type AddSliceContainerProps = { - datasources: Datasource[]; +type Dataset = { + id: number; + table_name: string; + description: string; + datasource_type: string; }; +export type AddSliceContainerProps = {}; + export type AddSliceContainerState = { - datasourceId?: string; - datasourceType?: string; - datasourceValue?: string; + datasource?: { label: string; value: string }; visType: string | null; }; @@ -81,6 +87,34 @@ const StyledContainer = styled.div` margin-top: ${theme.gridUnit * 6}px; } } + + & .ant-tooltip-open { + display: inline; + } + `} +`; + +const TooltipContent = styled.div<{ hasDescription: boolean }>` + ${({ theme, hasDescription }) => ` + .tooltip-header { + font-size: ${ + hasDescription ? theme.typography.sizes.l : theme.typography.sizes.s + }px; + font-weight: ${ + hasDescription + ? theme.typography.weights.bold + : theme.typography.weights.normal + }; + } + + .tooltip-description { + margin-top: ${theme.gridUnit * 2}px; + display: -webkit-box; + -webkit-line-clamp: 20; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + } `} `; @@ -110,13 +144,15 @@ export default class AddSliceContainer extends React.PureComponent< this.changeDatasource = this.changeDatasource.bind(this); this.changeVisType = this.changeVisType.bind(this); this.gotoSlice = this.gotoSlice.bind(this); + this.newLabel = this.newLabel.bind(this); + this.loadDatasources = this.loadDatasources.bind(this); } exploreUrl() { const formData = encodeURIComponent( JSON.stringify({ viz_type: this.state.visType, - datasource: this.state.datasourceValue, + datasource: this.state.datasource?.value, }), ); return `/superset/explore/?form_data=${formData}`; @@ -126,11 +162,8 @@ export default class AddSliceContainer extends React.PureComponent< window.location.href = this.exploreUrl(); } - changeDatasource(value: string) { - this.setState({ - datasourceValue: value, - datasourceId: value.split('__')[0], - }); + changeDatasource(datasource: { label: string; value: string }) { + this.setState({ datasource }); } changeVisType(visType: string | null) { @@ -138,7 +171,45 @@ export default class AddSliceContainer extends React.PureComponent< } isBtnDisabled() { - return !(this.state.datasourceId && this.state.visType); + return !(this.state.datasource?.value && this.state.visType); + } + + newLabel(item: Dataset) { + return ( + +
{item.table_name}
+ {item.description && ( +
{item.description}
+ )} + + } + > + {item.table_name} +
+ ); + } + + loadDatasources(search: string, page: number, pageSize: number) { + const query = rison.encode({ + columns: ['id', 'table_name', 'description', 'datasource_type'], + filter: search, + page, + page_size: pageSize, + }); + return SupersetClient.get({ + endpoint: `/api/v1/dataset?q=${query}`, + }).then((response: JsonResponse) => { + const list = response.json.result.map((item: Dataset) => ({ + value: `${item.id}__${item.datasource_type}`, + label: this.newLabel(item), + })); + return { + data: list, + totalCount: response.json.count, + }; + }); } render() { @@ -152,10 +223,10 @@ export default class AddSliceContainer extends React.PureComponent< name="select-datasource" header={{t('Choose a dataset')}} onChange={this.changeDatasource} - options={this.props.datasources} + options={this.loadDatasources} placeholder={t('Choose a dataset')} showSearch - value={this.state.datasourceValue} + value={this.state.datasource} /> {t( diff --git a/superset-frontend/src/addSlice/App.tsx b/superset-frontend/src/addSlice/App.tsx index 9602670a08902..900cc36f446c8 100644 --- a/superset-frontend/src/addSlice/App.tsx +++ b/superset-frontend/src/addSlice/App.tsx @@ -39,7 +39,7 @@ initFeatureFlags(bootstrapData.common.feature_flags); const App = () => ( - + ); diff --git a/superset/datasets/api.py b/superset/datasets/api.py index 405f985493523..35d09448a397e 100644 --- a/superset/datasets/api.py +++ b/superset/datasets/api.py @@ -100,6 +100,8 @@ class DatasetRestApi(BaseSupersetModelRestApi): "changed_on_utc", "changed_on_delta_humanized", "default_endpoint", + "description", + "datasource_type", "explore_url", "extra", "kind", diff --git a/tests/integration_tests/datasets/api_tests.py b/tests/integration_tests/datasets/api_tests.py index 5275a87173030..385025e3835cc 100644 --- a/tests/integration_tests/datasets/api_tests.py +++ b/tests/integration_tests/datasets/api_tests.py @@ -179,7 +179,9 @@ def test_get_dataset_list(self): "changed_on_delta_humanized", "changed_on_utc", "database", + "datasource_type", "default_endpoint", + "description", "explore_url", "extra", "id",