feat: Flow for tables that already have a dataset (#22136)

This commit is contained in:
Lyndsi Kay Williams 2022-12-05 15:43:54 -06:00 committed by GitHub
parent 96de314c0c
commit 04b7a26365
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 366 additions and 82 deletions

View File

@ -24,7 +24,7 @@ import DatasetPanel, {
tableColumnDefinition,
COLUMN_TITLE,
} from './DatasetPanel';
import { exampleColumns } from './fixtures';
import { exampleColumns, exampleDataset } from './fixtures';
import {
SELECT_MESSAGE,
CREATE_MESSAGE,
@ -44,7 +44,7 @@ jest.mock(
);
describe('DatasetPanel', () => {
it('renders a blank state DatasetPanel', () => {
test('renders a blank state DatasetPanel', () => {
render(<DatasetPanel hasError={false} columnList={[]} loading={false} />);
const blankDatasetImg = screen.getByRole('img', { name: /empty/i });
@ -65,7 +65,7 @@ describe('DatasetPanel', () => {
expect(sqlLabLink).toBeVisible();
});
it('renders a no columns screen', () => {
test('renders a no columns screen', () => {
render(
<DatasetPanel
tableName="Name"
@ -83,7 +83,7 @@ describe('DatasetPanel', () => {
expect(noColumnsDescription).toBeVisible();
});
it('renders a loading screen', () => {
test('renders a loading screen', () => {
render(
<DatasetPanel
tableName="Name"
@ -99,7 +99,7 @@ describe('DatasetPanel', () => {
expect(blankDatasetTitle).toBeVisible();
});
it('renders an error screen', () => {
test('renders an error screen', () => {
render(
<DatasetPanel
tableName="Name"
@ -115,7 +115,7 @@ describe('DatasetPanel', () => {
expect(errorDescription).toBeVisible();
});
it('renders a table with columns displayed', async () => {
test('renders a table with columns displayed', async () => {
const tableName = 'example_name';
render(
<DatasetPanel
@ -138,4 +138,23 @@ describe('DatasetPanel', () => {
expect(screen.getByText(row.type)).toBeInTheDocument();
});
});
test('renders an info banner if table already has a dataset', async () => {
render(
<DatasetPanel
tableName="example_table"
hasError={false}
columnList={exampleColumns}
loading={false}
datasets={exampleDataset}
/>,
);
// This is text in the info banner
expect(
await screen.findByText(
/this table already has a dataset associated with it. you can only associate one dataset with a table./i,
),
).toBeVisible();
});
});

View File

@ -17,12 +17,14 @@
* under the License.
*/
import React from 'react';
import { supersetTheme, t, styled } from '@superset-ui/core';
import { t, styled, useTheme } from '@superset-ui/core';
import Icons from 'src/components/Icons';
import Alert from 'src/components/Alert';
import Table, { ColumnsType, TableSize } from 'src/components/Table';
import { alphabeticalSort } from 'src/components/Table/sorters';
// @ts-ignore
import LOADING_GIF from 'src/assets/images/loading.gif';
import { DatasetObject } from 'src/views/CRUD/data/dataset/AddDataset/types';
import { ITableColumn } from './types';
import MessageContent from './MessageContent';
@ -53,43 +55,56 @@ const HALF = 0.5;
const MARGIN_MULTIPLIER = 3;
const StyledHeader = styled.div<StyledHeaderProps>`
${({ theme }) => `
position: ${(props: StyledHeaderProps) => props.position};
margin: ${({ theme }) => theme.gridUnit * MARGIN_MULTIPLIER}px;
margin-top: ${({ theme }) => theme.gridUnit * (MARGIN_MULTIPLIER + 1)}px;
font-size: ${({ theme }) => theme.gridUnit * 6}px;
font-weight: ${({ theme }) => theme.typography.weights.medium};
padding-bottom: ${({ theme }) => theme.gridUnit * MARGIN_MULTIPLIER}px;
margin: ${theme.gridUnit * (MARGIN_MULTIPLIER + 1)}px
${theme.gridUnit * MARGIN_MULTIPLIER}px
${theme.gridUnit * MARGIN_MULTIPLIER}px
${theme.gridUnit * (MARGIN_MULTIPLIER + 3)}px;
font-size: ${theme.gridUnit * 6}px;
font-weight: ${theme.typography.weights.medium};
padding-bottom: ${theme.gridUnit * MARGIN_MULTIPLIER}px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.anticon:first-of-type {
margin-right: ${({ theme }) => theme.gridUnit * (MARGIN_MULTIPLIER + 1)}px;
margin-right: ${theme.gridUnit * (MARGIN_MULTIPLIER + 1)}px;
}
.anticon:nth-of-type(2) {
margin-left: ${({ theme }) => theme.gridUnit * (MARGIN_MULTIPLIER + 1)}px;
}
margin-left: ${theme.gridUnit * (MARGIN_MULTIPLIER + 1)}px;
`}
`;
const StyledTitle = styled.div`
margin-left: ${({ theme }) => theme.gridUnit * MARGIN_MULTIPLIER}px;
margin-bottom: ${({ theme }) => theme.gridUnit * MARGIN_MULTIPLIER}px;
font-weight: ${({ theme }) => theme.typography.weights.bold};
${({ theme }) => `
margin-left: ${theme.gridUnit * (MARGIN_MULTIPLIER + 3)}px;
margin-bottom: ${theme.gridUnit * MARGIN_MULTIPLIER}px;
font-weight: ${theme.typography.weights.bold};
`}
`;
const LoaderContainer = styled.div`
padding: ${({ theme }) => theme.gridUnit * 8}px
${({ theme }) => theme.gridUnit * 6}px;
${({ theme }) => `
padding: ${theme.gridUnit * 8}px
${theme.gridUnit * 6}px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
`}
`;
const StyledLoader = styled.div`
${({ theme }) => `
max-width: 50%;
width: ${LOADER_WIDTH}px;
@ -100,19 +115,23 @@ const StyledLoader = styled.div`
div {
width: 100%;
margin-top: ${({ theme }) => theme.gridUnit * MARGIN_MULTIPLIER}px;
margin-top: ${theme.gridUnit * MARGIN_MULTIPLIER}px;
text-align: center;
font-weight: ${({ theme }) => theme.typography.weights.normal};
font-size: ${({ theme }) => theme.typography.sizes.l}px;
color: ${({ theme }) => theme.colors.grayscale.light1};
font-weight: ${theme.typography.weights.normal};
font-size: ${theme.typography.sizes.l}px;
color: ${theme.colors.grayscale.light1};
}
`}
`;
const TableContainer = styled.div`
${({ theme }) => `
position: relative;
margin: ${({ theme }) => theme.gridUnit * MARGIN_MULTIPLIER}px;
margin: ${theme.gridUnit * MARGIN_MULTIPLIER}px;
margin-left: ${theme.gridUnit * (MARGIN_MULTIPLIER + 3)}px;
overflow: scroll;
height: calc(100% - ${({ theme }) => theme.gridUnit * 36}px);
height: calc(100% - ${theme.gridUnit * 36}px);
`}
`;
const StyledTable = styled(Table)`
@ -123,6 +142,26 @@ const StyledTable = styled(Table)`
right: 0;
`;
const StyledAlert = styled(Alert)`
${({ theme }) => `
border: 1px solid ${theme.colors.info.base};
padding: ${theme.gridUnit * 4}px;
margin: ${theme.gridUnit * 6}px ${theme.gridUnit * 6}px
${theme.gridUnit * 8}px;
.view-dataset-button {
position: absolute;
top: ${theme.gridUnit * 4}px;
right: ${theme.gridUnit * 4}px;
font-weight: ${theme.typography.weights.normal};
&:hover {
color: ${theme.colors.secondary.dark3};
text-decoration: underline;
}
}
`}
`;
export const REFRESHING = t('Refreshing columns');
export const COLUMN_TITLE = t('Table columns');
export const ALT_LOADING = t('Loading');
@ -168,19 +207,57 @@ export interface IDatasetPanelProps {
* Boolean indicating if the component is in a loading state
*/
loading: boolean;
datasets?: DatasetObject[] | undefined;
}
const EXISTING_DATASET_DESCRIPTION = t(
'This table already has a dataset associated with it. You can only associate one dataset with a table.\n',
);
const VIEW_DATASET = t('View Dataset');
const renderExistingDatasetAlert = (dataset?: DatasetObject) => (
<StyledAlert
closable={false}
type="info"
showIcon
message={t('This table already has a dataset')}
description={
<>
{EXISTING_DATASET_DESCRIPTION}
<span
role="button"
onClick={() => {
window.open(
dataset?.explore_url,
'_blank',
'noreferrer noopener popup=false',
);
}}
tabIndex={0}
className="view-dataset-button"
>
{VIEW_DATASET}
</span>
</>
}
/>
);
const DatasetPanel = ({
tableName,
columnList,
loading,
hasError,
datasets,
}: IDatasetPanelProps) => {
const theme = useTheme();
const hasColumns = columnList?.length > 0 ?? false;
const datasetNames = datasets?.map(dataset => dataset.table_name);
let component;
let loader;
if (loading) {
component = (
loader = (
<LoaderContainer>
<StyledLoader>
<img alt={ALT_LOADING} src={LOADING_GIF} />
@ -188,48 +265,58 @@ const DatasetPanel = ({
</StyledLoader>
</LoaderContainer>
);
} else if (tableName && hasColumns && !hasError) {
component = (
<>
<StyledTitle>{COLUMN_TITLE}</StyledTitle>
<TableContainer>
<StyledTable
loading={loading}
size={TableSize.SMALL}
columns={tableColumnDefinition}
data={columnList}
pageSizeOptions={pageSizeOptions}
defaultPageSize={10}
/>
</TableContainer>
</>
);
} else {
component = (
<MessageContent
hasColumns={hasColumns}
hasError={hasError}
tableName={tableName}
/>
);
}
if (!loading) {
if (!loading && tableName && hasColumns && !hasError) {
component = (
<>
<StyledTitle>{COLUMN_TITLE}</StyledTitle>
<TableContainer>
<StyledTable
loading={loading}
size={TableSize.SMALL}
columns={tableColumnDefinition}
data={columnList}
pageSizeOptions={pageSizeOptions}
defaultPageSize={10}
/>
</TableContainer>
</>
);
} else {
component = (
<MessageContent
hasColumns={hasColumns}
hasError={hasError}
tableName={tableName}
/>
);
}
}
return (
<>
{tableName && (
<StyledHeader
position={
!loading && hasColumns ? EPosition.RELATIVE : EPosition.ABSOLUTE
}
title={tableName || ''}
>
{tableName && (
<Icons.Table iconColor={supersetTheme.colors.grayscale.base} />
)}
{tableName}
</StyledHeader>
<>
{datasetNames?.includes(tableName) &&
renderExistingDatasetAlert(
datasets?.find(dataset => dataset.table_name === tableName),
)}
<StyledHeader
position={
!loading && hasColumns ? EPosition.RELATIVE : EPosition.ABSOLUTE
}
title={tableName || ''}
>
{tableName && (
<Icons.Table iconColor={theme.colors.grayscale.base} />
)}
{tableName}
</StyledHeader>
</>
)}
{component}
{loader}
</>
);
};

View File

@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DatasetObject } from 'src/views/CRUD/data/dataset/AddDataset/types';
import { ITableColumn } from './types';
export const exampleColumns: ITableColumn[] = [
@ -32,3 +33,16 @@ export const exampleColumns: ITableColumn[] = [
type: 'DATE',
},
];
export const exampleDataset: DatasetObject[] = [
{
db: {
id: 1,
database_name: 'test_database',
owners: [1],
},
schema: 'test_schema',
dataset_name: 'example_dataset',
table_name: 'example_table',
},
];

View File

@ -18,6 +18,7 @@
*/
import React, { useEffect, useState, useRef } from 'react';
import { SupersetClient } from '@superset-ui/core';
import { DatasetObject } from 'src/views/CRUD/data/dataset/AddDataset/types';
import DatasetPanel from './DatasetPanel';
import { ITableColumn, IDatabaseTable, isIDatabaseTable } from './types';
@ -53,6 +54,7 @@ export interface IDatasetPanelWrapperProps {
*/
schema?: string | null;
setHasColumns?: Function;
datasets?: DatasetObject[] | undefined;
}
const DatasetPanelWrapper = ({
@ -60,6 +62,7 @@ const DatasetPanelWrapper = ({
dbId,
schema,
setHasColumns,
datasets,
}: IDatasetPanelWrapperProps) => {
const [columnList, setColumnList] = useState<ITableColumn[]>([]);
const [loading, setLoading] = useState(false);
@ -110,7 +113,7 @@ const DatasetPanelWrapper = ({
if (tableName && schema && dbId) {
getTableMetadata({ tableName, dbId, schema });
}
// getTableMetadata is a const and should not be independency array
// getTableMetadata is a const and should not be in dependency array
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tableName, dbId, schema]);
@ -120,6 +123,7 @@ const DatasetPanelWrapper = ({
hasError={hasError}
loading={loading}
tableName={tableName}
datasets={datasets}
/>
);
};

View File

@ -40,7 +40,7 @@ const mockPropsWithDataset = {
};
describe('Footer', () => {
it('renders a Footer with a cancel button and a disabled create button', () => {
test('renders a Footer with a cancel button and a disabled create button', () => {
render(<Footer {...mockedProps} />, { useRedux: true });
const saveButton = screen.getByRole('button', {
@ -55,7 +55,7 @@ describe('Footer', () => {
expect(createButton).toBeDisabled();
});
it('renders a Create Dataset button when a table is selected', () => {
test('renders a Create Dataset button when a table is selected', () => {
render(<Footer {...mockPropsWithDataset} />, { useRedux: true });
const createButton = screen.getByRole('button', {
@ -64,4 +64,16 @@ describe('Footer', () => {
expect(createButton).toBeEnabled();
});
test('create button becomes disabled when table already has a dataset', () => {
render(<Footer datasets={['real_info']} {...mockPropsWithDataset} />, {
useRedux: true,
});
const createButton = screen.getByRole('button', {
name: /Create/i,
});
expect(createButton).toBeDisabled();
});
});

View File

@ -37,6 +37,7 @@ interface FooterProps {
datasetObject?: Partial<DatasetObject> | null;
onDatasetAdd?: (dataset: DatasetObject) => void;
hasColumns?: boolean;
datasets?: (string | null | undefined)[] | undefined;
}
const INPUT_FIELDS = ['db', 'schema', 'table_name'];
@ -52,6 +53,7 @@ function Footer({
datasetObject,
addDangerToast,
hasColumns = false,
datasets,
}: FooterProps) {
const { createResource } = useSingleViewResource<Partial<DatasetObject>>(
'dataset',
@ -108,16 +110,22 @@ function Footer({
}
};
const CREATE_DATASET_TEXT = t('Create Dataset');
const disabledCheck =
!datasetObject?.table_name ||
!hasColumns ||
datasets?.includes(datasetObject?.table_name);
return (
<>
<Button onClick={cancelButtonOnClick}>Cancel</Button>
<Button
buttonStyle="primary"
disabled={!datasetObject?.table_name || !hasColumns}
disabled={disabledCheck}
tooltip={!datasetObject?.table_name ? tooltipText : undefined}
onClick={onSave}
>
{t('Create Dataset')}
{CREATE_DATASET_TEXT}
</Button>
</>
);

View File

@ -209,6 +209,7 @@ test('searches for a table name', async () => {
useRedux: true,
});
// Click 'test-postgres' database to access schemas
const databaseSelect = screen.getByRole('combobox', {
name: /select database or type database name/i,
});
@ -221,6 +222,7 @@ test('searches for a table name', async () => {
await waitFor(() => expect(schemaSelect).toBeEnabled());
// Click 'public' schema to access tables
userEvent.click(schemaSelect);
userEvent.click(screen.getAllByText('public')[1]);
@ -238,3 +240,46 @@ test('searches for a table name', async () => {
expect(screen.queryByText('Sheet3')).not.toBeInTheDocument();
});
});
test('renders a warning icon when a table name has a pre-existing dataset', async () => {
render(
<LeftPanel
setDataset={mockFun}
schema="schema_a"
dbId={1}
datasets={['Sheet2']}
/>,
{
useRedux: true,
},
);
// Click 'test-postgres' database to access schemas
const databaseSelect = screen.getByRole('combobox', {
name: /select database or type database name/i,
});
userEvent.click(databaseSelect);
userEvent.click(await screen.findByText('test-postgres'));
const schemaSelect = screen.getByRole('combobox', {
name: /select schema or type schema name/i,
});
await waitFor(() => expect(schemaSelect).toBeEnabled());
// Warning icon should not show yet
expect(
screen.queryByRole('img', { name: 'warning' }),
).not.toBeInTheDocument();
// Click 'public' schema to access tables
userEvent.click(schemaSelect);
userEvent.click(screen.getAllByText('public')[1]);
await waitFor(() => {
expect(screen.getByText('Sheet2')).toBeInTheDocument();
});
// Sheet2 should now show the warning icon
expect(screen.getByRole('img', { name: 'warning' })).toBeVisible();
});

View File

@ -17,7 +17,14 @@
* under the License.
*/
import React, { useEffect, useState, SetStateAction, Dispatch } from 'react';
import { SupersetClient, t, styled } from '@superset-ui/core';
import {
SupersetClient,
t,
styled,
css,
useTheme,
logging,
} from '@superset-ui/core';
import { Input } from 'src/components/Input';
import { Form } from 'src/components/Form';
import Icons from 'src/components/Icons';
@ -36,6 +43,7 @@ interface LeftPanelProps {
setDataset: Dispatch<SetStateAction<object>>;
schema?: string | null | undefined;
dbId?: number;
datasets?: (string | null | undefined)[] | undefined;
}
const SearchIcon = styled(Icons.Search)`
@ -78,6 +86,7 @@ const LeftPanelStyle = styled.div`
top: ${theme.gridUnit * 92.25}px;
left: ${theme.gridUnit * 3.25}px;
right: 0;
.options {
cursor: pointer;
padding: ${theme.gridUnit * 1.75}px;
@ -86,6 +95,7 @@ const LeftPanelStyle = styled.div`
background-color: ${theme.colors.grayscale.light4}
}
}
.options-highlighted {
cursor: pointer;
padding: ${theme.gridUnit * 1.75}px;
@ -93,6 +103,12 @@ const LeftPanelStyle = styled.div`
background-color: ${theme.colors.primary.dark1};
color: ${theme.colors.grayscale.light5};
}
.options, .options-highlighted {
display: flex;
align-items: center;
justify-content: space-between;
}
}
form > span[aria-label="refresh"] {
position: absolute;
@ -124,7 +140,10 @@ export default function LeftPanel({
setDataset,
schema,
dbId,
datasets,
}: LeftPanelProps) {
const theme = useTheme();
const [tableOptions, setTableOptions] = useState<Array<TableOption>>([]);
const [resetTables, setResetTables] = useState(false);
const [loadTables, setLoadTables] = useState(false);
@ -166,9 +185,9 @@ export default function LeftPanel({
setResetTables(false);
setRefresh(false);
})
.catch(e => {
console.log('error', e);
});
.catch(error =>
logging.error('There was an error fetching tables', error),
);
};
const setSchema = (schema: string) => {
@ -212,21 +231,32 @@ export default function LeftPanel({
</div>
);
const SELECT_DATABASE_AND_SCHEMA_TEXT = t('Select database & schema');
const TABLE_LOADING_TEXT = t('Table loading');
const NO_TABLES_FOUND_TITLE = t('No database tables found');
const NO_TABLES_FOUND_DESCRIPTION = t('Try selecting a different schema');
const SELECT_DATABASE_TABLE_TEXT = t('Select database table');
const REFRESH_TABLE_LIST_TOOLTIP = t('Refresh table list');
const REFRESH_TABLES_TEXT = t('Refresh tables');
const SEARCH_TABLES_PLACEHOLDER_TEXT = t('Search tables');
return (
<LeftPanelStyle>
<p className="section-title db-schema">Select database & schema</p>
<p className="section-title db-schema">
{SELECT_DATABASE_AND_SCHEMA_TEXT}
</p>
<DatabaseSelector
handleError={addDangerToast}
onDbChange={setDatabase}
onSchemaChange={setSchema}
/>
{loadTables && !refresh && Loader('Table loading')}
{loadTables && !refresh && Loader(TABLE_LOADING_TEXT)}
{schema && !loadTables && !tableOptions.length && !searchVal && (
<div className="emptystate">
<EmptyStateMedium
image="empty-table.svg"
title={t('No database tables found')}
description={t('Try selecting a different schema')}
title={NO_TABLES_FOUND_TITLE}
description={NO_TABLES_FOUND_DESCRIPTION}
/>
</div>
)}
@ -234,15 +264,15 @@ export default function LeftPanel({
{schema && (tableOptions.length > 0 || searchVal.length > 0) && (
<>
<Form>
<p className="table-title">Select database table</p>
<p className="table-title">{SELECT_DATABASE_TABLE_TEXT}</p>
<RefreshLabel
onClick={() => {
setLoadTables(true);
setRefresh(true);
}}
tooltipContent={t('Refresh table list')}
tooltipContent={REFRESH_TABLE_LIST_TOOLTIP}
/>
{refresh && Loader('Refresh tables')}
{refresh && Loader(REFRESH_TABLES_TEXT)}
{!refresh && (
<Input
value={searchVal}
@ -251,7 +281,7 @@ export default function LeftPanel({
setSearchVal(evt.target.value);
}}
className="table-form"
placeholder={t('Search tables')}
placeholder={SEARCH_TABLES_PLACEHOLDER_TEXT}
allowClear
/>
)}
@ -269,6 +299,19 @@ export default function LeftPanel({
onClick={() => setTable(option.value, i)}
>
{option.label}
{datasets?.includes(option.value) && (
<Icons.Warning
iconColor={
selectedTable === i
? theme.colors.grayscale.light5
: theme.colors.info.base
}
iconSize="m"
css={css`
margin-right: ${theme.gridUnit * 6}px;
`}
/>
)}
</div>
))}
</div>

View File

@ -16,7 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useReducer, Reducer, useState } from 'react';
import React, { useReducer, Reducer, useEffect, useState } from 'react';
import { logging } from '@superset-ui/core';
import { UseGetDatasetsList } from 'src/views/CRUD/data/hooks';
import rison from 'rison';
import Header from './Header';
import DatasetPanel from './DatasetPanel';
import LeftPanel from './LeftPanel';
@ -73,6 +76,36 @@ export default function AddDataset() {
Reducer<Partial<DatasetObject> | null, DSReducerActionType>
>(datasetReducer, null);
const [hasColumns, setHasColumns] = useState(false);
const [datasets, setDatasets] = useState<DatasetObject[]>([]);
const datasetNames = datasets.map(dataset => dataset.table_name);
const encodedSchema = dataset?.schema
? encodeURIComponent(dataset?.schema)
: undefined;
const queryParams = dataset?.schema
? rison.encode_uri({
filters: [
{ col: 'schema', opr: 'eq', value: encodedSchema },
{ col: 'sql', opr: 'dataset_is_null_or_empty', value: '!t' },
],
})
: undefined;
const getDatasetsList = async () => {
await UseGetDatasetsList(queryParams)
.then(json => {
setDatasets(json?.result);
})
.catch(error =>
logging.error('There was an error fetching dataset', error),
);
};
useEffect(() => {
if (dataset?.schema) {
getDatasetsList();
}
}, [dataset?.schema]);
const HeaderComponent = () => (
<Header setDataset={setDataset} title={dataset?.table_name} />
@ -83,6 +116,7 @@ export default function AddDataset() {
setDataset={setDataset}
schema={dataset?.schema}
dbId={dataset?.db?.id}
datasets={datasetNames}
/>
);
@ -92,11 +126,17 @@ export default function AddDataset() {
dbId={dataset?.db?.id}
schema={dataset?.schema}
setHasColumns={setHasColumns}
datasets={datasets}
/>
);
const FooterComponent = () => (
<Footer url={prevUrl} datasetObject={dataset} hasColumns={hasColumns} />
<Footer
url={prevUrl}
datasetObject={dataset}
hasColumns={hasColumns}
datasets={datasetNames}
/>
);
return (

View File

@ -32,6 +32,7 @@ export interface DatasetObject {
schema?: string | null;
dataset_name: string;
table_name?: string | null;
explore_url?: string;
}
export interface DatasetReducerPayloadType {

View File

@ -82,6 +82,7 @@ export const StyledLayoutLeftPanel = styled.div`
export const StyledLayoutDatasetPanel = styled.div`
width: 100%;
position: relative;
`;
export const StyledLayoutRightPanel = styled.div`

View File

@ -17,6 +17,7 @@
* under the License.
*/
import { useState, useEffect } from 'react';
import { SupersetClient, logging } from '@superset-ui/core';
type BaseQueryObject = {
id: number;
@ -73,3 +74,12 @@ export function useQueryPreviewState<D extends BaseQueryObject = any>({
disableNext,
};
}
export const UseGetDatasetsList = (queryParams: string | undefined) =>
SupersetClient.get({
endpoint: `/api/v1/dataset/?q=${queryParams}`,
})
.then(({ json }) => json)
.catch(error =>
logging.error('There was an error fetching dataset', error),
);