feat: Enable new dataset creation flow (#22610)

This commit is contained in:
Lyndsi Kay Williams 2023-01-17 20:05:40 -06:00 committed by GitHub
parent e05ac858f4
commit c87f654901
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 175 additions and 347 deletions

View File

@ -186,27 +186,6 @@ test('should render a create dataset infobox', async () => {
expect(infoboxText).toBeVisible();
});
test('should render a save dataset modal when "Create a dataset" is clicked', async () => {
const newProps = {
...props,
datasource: {
...datasource,
type: DatasourceType.Query,
},
};
render(<DatasourcePanel {...newProps} />, { useRedux: true, useDnd: true });
const createButton = await screen.findByRole('button', {
name: /create a dataset/i,
});
userEvent.click(createButton);
const saveDatasetModalTitle = screen.getByText(/save or overwrite dataset/i);
expect(saveDatasetModalTitle).toBeVisible();
});
test('should not render a save dataset modal when datasource is not query or dataset', async () => {
const newProps = {
...props,

View File

@ -231,7 +231,9 @@ const ColumnSelectPopover = ({
}, []);
const setDatasetAndClose = () => {
if (setDatasetModal) setDatasetModal(true);
if (setDatasetModal) {
setDatasetModal(true);
}
onClose();
};

View File

@ -337,10 +337,7 @@ export class ChartCreation extends React.PureComponent<
const isButtonDisabled = this.isBtnDisabled();
const datasetHelpText = this.state.canCreateDataset ? (
<span data-test="dataset-write">
<Link
to="/tablemodelview/list/#create"
data-test="add-chart-new-dataset"
>
<Link to="/dataset/add/" data-test="add-chart-new-dataset">
{t('Add a dataset')}
</Link>
{` ${t('or')} `}

View File

@ -21,6 +21,7 @@ import React, { useState, useMemo, useEffect } from 'react';
import rison from 'rison';
import { useSelector } from 'react-redux';
import { useQueryParams, BooleanParam } from 'use-query-params';
import { LocalStorageKeys, setItem } from 'src/utils/localStorageHelpers';
import Loading from 'src/components/Loading';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
@ -157,6 +158,9 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
refreshData();
addSuccessToast(t('Deleted: %s', dbName));
// Delete user-selected db from local storage
setItem(LocalStorageKeys.db, null);
// Close delete modal
setDatabaseCurrentlyDeleting(null);
},

View File

@ -43,6 +43,14 @@ jest.mock('@superset-ui/core', () => ({
isFeatureEnabled: () => true,
}));
const mockHistoryPush = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: mockHistoryPush,
}),
}));
const dbProps = {
show: true,
database_name: 'my database',

View File

@ -31,6 +31,7 @@ import React, {
useReducer,
Reducer,
} from 'react';
import { useHistory } from 'react-router-dom';
import { setItem, LocalStorageKeys } from 'src/utils/localStorageHelpers';
import { UploadChangeParam, UploadFile } from 'antd/lib/upload/interface';
import Tabs from 'src/components/Tabs';
@ -518,6 +519,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
t('database'),
addDangerToast,
);
const history = useHistory();
const [tabKey, setTabKey] = useState<string>(DEFAULT_TAB_KEY);
const [availableDbs, getAvailableDbs] = useAvailableDatabases();
@ -1295,7 +1297,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
onClick={() => {
setLoading(true);
fetchAndSetDB();
window.location.href = '/tablemodelview/list#create';
history.push('/dataset/add/');
}}
>
{t('CREATE DATASET')}
@ -1306,7 +1308,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
onClick={() => {
setLoading(true);
fetchAndSetDB();
window.location.href = `/superset/sqllab/?db=true`;
history.push(`/superset/sqllab/?db=true`);
}}
>
{t('QUERY DATA IN SQL LAB')}

View File

@ -20,6 +20,14 @@ import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import AddDataset from 'src/views/CRUD/data/dataset/AddDataset';
const mockHistoryPush = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: mockHistoryPush,
}),
}));
describe('AddDataset', () => {
it('renders a blank state AddDataset', async () => {
render(<AddDataset />, { useRedux: true });

View File

@ -40,6 +40,7 @@ export const exampleDataset: DatasetObject[] = [
id: 1,
database_name: 'test_database',
owners: [1],
backend: 'test_backend',
},
schema: 'test_schema',
dataset_name: 'example_dataset',

View File

@ -17,8 +17,9 @@
* under the License.
*/
import React, { useEffect, useState, useRef } from 'react';
import { SupersetClient } from '@superset-ui/core';
import { SupersetClient, logging, t } from '@superset-ui/core';
import { DatasetObject } from 'src/views/CRUD/data/dataset/AddDataset/types';
import { addDangerToast } from 'src/components/MessageToasts/actions';
import DatasetPanel from './DatasetPanel';
import { ITableColumn, IDatabaseTable, isIDatabaseTable } from './types';
@ -94,9 +95,17 @@ const DatasetPanelWrapper = ({
setColumnList([]);
setHasColumns?.(false);
setHasError(true);
// eslint-disable-next-line no-console
console.error(
`The API response from ${path} does not match the IDatabaseTable interface.`,
addDangerToast(
t(
'The API response from %s does not match the IDatabaseTable interface.',
path,
),
);
logging.error(
t(
'The API response from %s does not match the IDatabaseTable interface.',
path,
),
);
}
} catch (error) {

View File

@ -20,6 +20,14 @@ import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import Footer from 'src/views/CRUD/data/dataset/AddDataset/Footer';
const mockHistoryPush = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: mockHistoryPush,
}),
}));
const mockedProps = {
url: 'realwebsite.com',
};

View File

@ -17,6 +17,7 @@
* under the License.
*/
import React from 'react';
import { useHistory } from 'react-router-dom';
import Button from 'src/components/Button';
import { t } from '@superset-ui/core';
import { useSingleViewResource } from 'src/views/CRUD/hooks';
@ -49,12 +50,12 @@ const LOG_ACTIONS = [
];
function Footer({
url,
datasetObject,
addDangerToast,
hasColumns = false,
datasets,
}: FooterProps) {
const history = useHistory();
const { createResource } = useSingleViewResource<Partial<DatasetObject>>(
'dataset',
t('dataset'),
@ -72,11 +73,6 @@ function Footer({
return LOG_ACTIONS[value];
};
const goToPreviousUrl = () => {
// this is a placeholder url until the final feature gets implemented
// at that point we will be passing in the url of the previous location.
window.location.href = url;
};
const cancelButtonOnClick = () => {
if (!datasetObject) {
@ -85,7 +81,7 @@ function Footer({
const logAction = createLogAction(datasetObject);
logEvent(logAction, datasetObject);
}
goToPreviousUrl();
history.goBack();
};
const tooltipText = t('Select a database table.');
@ -104,13 +100,13 @@ function Footer({
if (typeof response === 'number') {
logEvent(LOG_ACTIONS_DATASET_CREATION_SUCCESS, datasetObject);
// When a dataset is created the response we get is its ID number
goToPreviousUrl();
history.push(`/chart/add/?dataset=${datasetObject.table_name}`);
}
});
}
};
const CREATE_DATASET_TEXT = t('Create Dataset');
const CREATE_DATASET_TEXT = t('Create Dataset and Create Chart');
const disabledCheck =
!datasetObject?.table_name ||
!hasColumns ||

View File

@ -21,6 +21,7 @@ import fetchMock from 'fetch-mock';
import userEvent from '@testing-library/user-event';
import { render, screen, waitFor } from 'spec/helpers/testing-library';
import LeftPanel from 'src/views/CRUD/data/dataset/AddDataset/LeftPanel';
import { exampleDataset } from 'src/views/CRUD/data/dataset/AddDataset/DatasetPanel/fixtures';
const databasesEndpoint = 'glob:*/api/v1/database/?q*';
const schemasEndpoint = 'glob:*/api/v1/database/*/schemas*';
@ -181,7 +182,7 @@ test('does not render blank state if there is nothing selected', async () => {
});
test('renders list of options when user clicks on schema', async () => {
render(<LeftPanel setDataset={mockFun} schema="schema_a" dbId={1} />, {
render(<LeftPanel setDataset={mockFun} dataset={exampleDataset[0]} />, {
useRedux: true,
});
@ -189,23 +190,21 @@ test('renders list of options when user clicks on schema', async () => {
const databaseSelect = screen.getByRole('combobox', {
name: 'Select database or type database name',
});
// Schema select should be disabled until database is selected
userEvent.click(databaseSelect);
expect(await screen.findByText('test-postgres')).toBeInTheDocument();
userEvent.click(screen.getByText('test-postgres'));
// Schema select will be automatically populated if there is only one schema
const schemaSelect = screen.getByRole('combobox', {
name: /select schema or type schema name/i,
});
userEvent.click(databaseSelect);
expect(await screen.findByText('test-postgres')).toBeInTheDocument();
expect(schemaSelect).toBeDisabled();
userEvent.click(screen.getByText('test-postgres'));
// Wait for schema field to be enabled
await waitFor(() => {
expect(schemaSelect).toBeEnabled();
});
});
test('searches for a table name', async () => {
render(<LeftPanel setDataset={mockFun} schema="schema_a" dbId={1} />, {
render(<LeftPanel setDataset={mockFun} dataset={exampleDataset[0]} />, {
useRedux: true,
});
@ -245,9 +244,8 @@ test('renders a warning icon when a table name has a pre-existing dataset', asyn
render(
<LeftPanel
setDataset={mockFun}
schema="schema_a"
dbId={1}
datasets={['Sheet2']}
dataset={exampleDataset[0]}
datasetNames={['Sheet2']}
/>,
{
useRedux: true,

View File

@ -16,7 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useEffect, useState, SetStateAction, Dispatch } from 'react';
import React, {
useEffect,
useState,
SetStateAction,
Dispatch,
useCallback,
} from 'react';
import {
SupersetClient,
t,
@ -40,13 +46,16 @@ import {
emptyStateComponent,
} from 'src/components/EmptyState';
import { useToasts } from 'src/components/MessageToasts/withToasts';
import { DatasetActionType } from '../types';
import { LocalStorageKeys, getItem } from 'src/utils/localStorageHelpers';
import {
DatasetActionType,
DatasetObject,
} from 'src/views/CRUD/data/dataset/AddDataset/types';
interface LeftPanelProps {
setDataset: Dispatch<SetStateAction<object>>;
schema?: string | null | undefined;
dbId?: number;
datasets?: (string | null | undefined)[] | undefined;
dataset?: Partial<DatasetObject> | null;
datasetNames?: (string | null | undefined)[] | undefined;
}
const SearchIcon = styled(Icons.Search)`
@ -145,9 +154,8 @@ const LeftPanelStyle = styled.div`
export default function LeftPanel({
setDataset,
schema,
dbId,
datasets,
dataset,
datasetNames,
}: LeftPanelProps) {
const theme = useTheme();
@ -160,11 +168,14 @@ export default function LeftPanel({
const { addDangerToast } = useToasts();
const setDatabase = (db: Partial<DatabaseObject>) => {
setDataset({ type: DatasetActionType.selectDatabase, payload: { db } });
setSelectedTable(null);
setResetTables(true);
};
const setDatabase = useCallback(
(db: Partial<DatabaseObject>) => {
setDataset({ type: DatasetActionType.selectDatabase, payload: { db } });
setSelectedTable(null);
setResetTables(true);
},
[setDataset],
);
const setTable = (tableName: string, index: number) => {
setSelectedTable(index);
@ -174,28 +185,32 @@ export default function LeftPanel({
});
};
const getTablesList = (url: string) => {
SupersetClient.get({ url })
.then(({ json }) => {
const options: TableOption[] = json.options.map((table: Table) => {
const option: TableOption = {
value: table.value,
label: <TableOption table={table} />,
text: table.label,
};
const getTablesList = useCallback(
(url: string) => {
SupersetClient.get({ url })
.then(({ json }) => {
const options: TableOption[] = json.options.map((table: Table) => {
const option: TableOption = {
value: table.value,
label: <TableOption table={table} />,
text: table.label,
};
return option;
return option;
});
setTableOptions(options);
setLoadTables(false);
setResetTables(false);
setRefresh(false);
})
.catch(error => {
addDangerToast(t('There was an error fetching tables'));
logging.error(t('There was an error fetching tables'), error);
});
setTableOptions(options);
setLoadTables(false);
setResetTables(false);
setRefresh(false);
})
.catch(error =>
logging.error('There was an error fetching tables', error),
);
};
},
[addDangerToast],
);
const setSchema = (schema: string) => {
if (schema) {
@ -209,16 +224,28 @@ export default function LeftPanel({
setResetTables(true);
};
const encodedSchema = schema ? encodeURIComponent(schema) : undefined;
const encodedSchema = dataset?.schema
? encodeURIComponent(dataset?.schema)
: undefined;
useEffect(() => {
const currentUserSelectedDb = getItem(
LocalStorageKeys.db,
null,
) as DatabaseObject;
if (currentUserSelectedDb) {
setDatabase(currentUserSelectedDb);
}
}, [setDatabase]);
useEffect(() => {
if (loadTables) {
const endpoint = encodeURI(
`/superset/tables/${dbId}/${encodedSchema}/${refresh}/`,
`/superset/tables/${dataset?.db?.id}/${encodedSchema}/${refresh}/`,
);
getTablesList(endpoint);
}
}, [loadTables]);
}, [loadTables, dataset?.db?.id, encodedSchema, getTablesList, refresh]);
useEffect(() => {
if (resetTables) {
@ -262,6 +289,7 @@ export default function LeftPanel({
{SELECT_DATABASE_AND_SCHEMA_TEXT}
</p>
<DatabaseSelector
db={dataset?.db}
handleError={addDangerToast}
onDbChange={setDatabase}
onSchemaChange={setSchema}
@ -269,7 +297,7 @@ export default function LeftPanel({
onEmptyResults={onEmptyResults}
/>
{loadTables && !refresh && Loader(TABLE_LOADING_TEXT)}
{schema && !loadTables && !tableOptions.length && !searchVal && (
{dataset?.schema && !loadTables && !tableOptions.length && !searchVal && (
<div className="emptystate">
<EmptyStateMedium
image="empty-table.svg"
@ -279,7 +307,7 @@ export default function LeftPanel({
</div>
)}
{schema && (tableOptions.length > 0 || searchVal.length > 0) && (
{dataset?.schema && (tableOptions.length > 0 || searchVal.length > 0) && (
<>
<Form>
<p className="table-title">{SELECT_DATABASE_TABLE_TEXT}</p>
@ -323,7 +351,7 @@ export default function LeftPanel({
onClick={() => setTable(option.value, i)}
>
{option.label}
{datasets?.includes(option.value) && (
{datasetNames?.includes(option.value) && (
<Icons.Warning
iconColor={
selectedTable === i

View File

@ -16,10 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useReducer, Reducer, useEffect, useState } from 'react';
import { logging } from '@superset-ui/core';
import React, {
useReducer,
Reducer,
useEffect,
useState,
useCallback,
} from 'react';
import { logging, t } from '@superset-ui/core';
import { UseGetDatasetsList } from 'src/views/CRUD/data/hooks';
import rison from 'rison';
import { addDangerToast } from 'src/components/MessageToasts/actions';
import Header from './Header';
import DatasetPanel from './DatasetPanel';
import LeftPanel from './LeftPanel';
@ -85,27 +92,29 @@ export default function AddDataset() {
const queryParams = dataset?.schema
? rison.encode_uri({
filters: [
{ col: 'database', opr: 'rel_o_m', value: dataset?.db?.id },
{ col: 'schema', opr: 'eq', value: encodedSchema },
{ col: 'sql', opr: 'dataset_is_null_or_empty', value: '!t' },
],
})
: undefined;
const getDatasetsList = async () => {
const getDatasetsList = useCallback(async () => {
await UseGetDatasetsList(queryParams)
.then(json => {
setDatasets(json?.result);
})
.catch(error =>
logging.error('There was an error fetching dataset', error),
);
};
.catch(error => {
addDangerToast(t('There was an error fetching dataset'));
logging.error(t('There was an error fetching dataset'), error);
});
}, [queryParams]);
useEffect(() => {
if (dataset?.schema) {
getDatasetsList();
}
}, [dataset?.schema]);
}, [dataset?.schema, getDatasetsList]);
const HeaderComponent = () => (
<Header setDataset={setDataset} title={dataset?.table_name} />
@ -114,9 +123,8 @@ export default function AddDataset() {
const LeftPanelComponent = () => (
<LeftPanel
setDataset={setDataset}
schema={dataset?.schema}
dbId={dataset?.db?.id}
datasets={datasetNames}
dataset={dataset}
datasetNames={datasetNames}
/>
);

View File

@ -16,6 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DatabaseObject } from 'src/components/DatabaseSelector';
export enum DatasetActionType {
selectDatabase,
selectSchema,
@ -24,11 +26,7 @@ export enum DatasetActionType {
}
export interface DatasetObject {
db: {
id: number;
database_name?: string;
owners?: number[];
};
db: DatabaseObject & { owners: [number] };
schema?: string | null;
dataset_name: string;
table_name?: string | null;

View File

@ -1,172 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { FunctionComponent, useState, useEffect } from 'react';
import { styled, t } from '@superset-ui/core';
import { useSingleViewResource } from 'src/views/CRUD/hooks';
import Modal from 'src/components/Modal';
import TableSelector from 'src/components/TableSelector';
import withToasts from 'src/components/MessageToasts/withToasts';
import { DatabaseObject } from 'src/components/DatabaseSelector';
import {
getItem,
LocalStorageKeys,
setItem,
} from 'src/utils/localStorageHelpers';
import { isEmpty } from 'lodash';
type DatasetAddObject = {
id: number;
database: number;
schema: string;
table_name: string;
};
interface DatasetModalProps {
addDangerToast: (msg: string) => void;
addSuccessToast: (msg: string) => void;
onDatasetAdd?: (dataset: DatasetAddObject) => void;
onHide: () => void;
show: boolean;
history?: any; // So we can render the modal when not using SPA
}
const TableSelectorContainer = styled.div`
padding-bottom: 340px;
width: 65%;
`;
const DatasetModal: FunctionComponent<DatasetModalProps> = ({
addDangerToast,
onDatasetAdd,
onHide,
show,
history,
}) => {
const [currentDatabase, setCurrentDatabase] = useState<
DatabaseObject | undefined
>();
const [currentSchema, setSchema] = useState<string | undefined>('');
const [currentTableName, setTableName] = useState('');
const [disableSave, setDisableSave] = useState(true);
const {
createResource,
state: { loading },
} = useSingleViewResource<Partial<DatasetAddObject>>(
'dataset',
t('dataset'),
addDangerToast,
);
useEffect(() => {
setDisableSave(currentDatabase === undefined || currentTableName === '');
}, [currentTableName, currentDatabase]);
useEffect(() => {
const currentUserSelectedDb = getItem(
LocalStorageKeys.db,
null,
) as DatabaseObject;
if (currentUserSelectedDb) setCurrentDatabase(currentUserSelectedDb);
}, []);
const onDbChange = (db: DatabaseObject) => {
setCurrentDatabase(db);
};
const onSchemaChange = (schema?: string) => {
setSchema(schema);
};
const onTableChange = (tableName: string) => {
setTableName(tableName);
};
const clearModal = () => {
setSchema('');
setTableName('');
setCurrentDatabase(undefined);
setDisableSave(true);
};
const cleanup = () => {
clearModal();
setItem(LocalStorageKeys.db, null);
};
const hide = () => {
cleanup();
onHide();
};
const onSave = () => {
if (currentDatabase === undefined) {
return;
}
const data = {
database: currentDatabase.id,
...(currentSchema ? { schema: currentSchema } : {}),
table_name: currentTableName,
};
createResource(data).then(response => {
if (!response) {
return;
}
if (onDatasetAdd) {
onDatasetAdd({ id: response.id, ...response });
}
// We need to be able to work with no SPA routes opening the modal
// So useHistory wont be available always thus we check for it
if (!isEmpty(history)) {
history?.push(`/chart/add?dataset=${currentTableName}`);
cleanup();
} else {
window.location.href = `/chart/add?dataset=${currentTableName}`;
cleanup();
onHide();
}
});
};
return (
<Modal
disablePrimaryButton={disableSave}
primaryButtonLoading={loading}
onHandledPrimaryAction={onSave}
onHide={hide}
primaryButtonName={t('Add Dataset and Create Chart')}
show={show}
title={t('Add dataset')}
>
<TableSelectorContainer>
<TableSelector
clearable={false}
formMode
database={currentDatabase}
schema={currentSchema}
tableValue={currentTableName}
onDbChange={onDbChange}
onSchemaChange={onSchemaChange}
onTableSelectChange={onTableChange}
handleError={addDangerToast}
/>
</TableSelectorContainer>
</Modal>
);
};
export default withToasts(DatasetModal);

View File

@ -25,6 +25,14 @@ import DatasetPanel from 'src/views/CRUD/data/dataset/AddDataset/DatasetPanel';
import RightPanel from 'src/views/CRUD/data/dataset/AddDataset/RightPanel';
import Footer from 'src/views/CRUD/data/dataset/AddDataset/Footer';
const mockHistoryPush = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: mockHistoryPush,
}),
}));
describe('DatasetLayout', () => {
it('renders nothing when no components are passed in', () => {
render(<DatasetLayout />);

View File

@ -22,16 +22,14 @@ import React, {
useState,
useMemo,
useCallback,
useEffect,
} from 'react';
import { useHistory } from 'react-router-dom';
import rison from 'rison';
import { useHistory, useLocation } from 'react-router-dom';
import {
createFetchRelated,
createFetchDistinct,
createErrorHandler,
} from 'src/views/CRUD/utils';
import { getItem, LocalStorageKeys } from 'src/utils/localStorageHelpers';
import { ColumnObject } from 'src/views/CRUD/data/dataset/types';
import { useListViewResource } from 'src/views/CRUD/hooks';
import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
@ -60,7 +58,6 @@ import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip';
import { isUserAdmin } from 'src/dashboard/util/permissionUtils';
import { GenericLink } from 'src/components/GenericLink/GenericLink';
import AddDatasetModal from './AddDatasetModal';
import {
PAGE_SIZE,
@ -139,6 +136,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
addSuccessToast,
user,
}) => {
const history = useHistory();
const {
state: {
loading,
@ -152,9 +150,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
refreshData,
} = useListViewResource<Dataset>('dataset', t('dataset'), addDangerToast);
const [datasetAddModalOpen, setDatasetAddModalOpen] =
useState<boolean>(false);
const [datasetCurrentlyDeleting, setDatasetCurrentlyDeleting] = useState<
(Dataset & { chart_count: number; dashboard_count: number }) | null
>(null);
@ -191,12 +186,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
hasPerm('can_export') && isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT);
const initialSort = SORT_BY;
useEffect(() => {
const db = getItem(LocalStorageKeys.db, null);
if (!loading && db) {
setDatasetAddModalOpen(true);
}
}, [loading]);
const openDatasetEditModal = useCallback(
({ id }: Dataset) => {
@ -603,26 +592,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
});
}
const CREATE_HASH = '#create';
const location = useLocation();
const history = useHistory();
// Sync Dataset Add modal with #create hash
useEffect(() => {
const modalOpen = location.hash === CREATE_HASH && canCreate;
setDatasetAddModalOpen(modalOpen);
}, [canCreate, location.hash]);
// Add #create hash
const openDatasetAddModal = useCallback(() => {
history.replace(`${location.pathname}${location.search}${CREATE_HASH}`);
}, [history, location.pathname, location.search]);
// Remove #create hash
const closeDatasetAddModal = useCallback(() => {
history.replace(`${location.pathname}${location.search}`);
}, [history, location.pathname, location.search]);
if (canCreate) {
buttonArr.push({
name: (
@ -630,7 +599,9 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
<i className="fa fa-plus" /> {t('Dataset')}{' '}
</>
),
onClick: openDatasetAddModal,
onClick: () => {
history.push('/dataset/add/');
},
buttonStyle: 'primary',
});
@ -727,12 +698,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
return (
<>
<SubMenu {...menuData} />
<AddDatasetModal
show={datasetAddModalOpen}
onHide={closeDatasetAddModal}
onDatasetAdd={refreshData}
history={history}
/>
{datasetCurrentlyDeleting && (
<DeleteModal
description={t(

View File

@ -17,7 +17,8 @@
* under the License.
*/
import { useState, useEffect } from 'react';
import { SupersetClient, logging } from '@superset-ui/core';
import { SupersetClient, logging, t } from '@superset-ui/core';
import { addDangerToast } from 'src/components/MessageToasts/actions';
type BaseQueryObject = {
id: number;
@ -80,6 +81,7 @@ export const UseGetDatasetsList = (queryParams: string | undefined) =>
endpoint: `/api/v1/dataset/?q=${queryParams}`,
})
.then(({ json }) => json)
.catch(error =>
logging.error('There was an error fetching dataset', error),
);
.catch(error => {
addDangerToast(t('There was an error fetching dataset'));
logging.error(t('There was an error fetching dataset'), error);
});

View File

@ -30,9 +30,6 @@ jest.mock('react-redux', () => ({
}));
jest.mock('src/views/CRUD/data/database/DatabaseModal', () => () => <span />);
jest.mock('src/views/CRUD/data/dataset/AddDatasetModal.tsx', () => () => (
<span />
));
const dropdownItems = [
{

View File

@ -51,7 +51,6 @@ import {
GlobalMenuDataOptions,
RightMenuProps,
} from './types';
import AddDatasetModal from '../CRUD/data/dataset/AddDatasetModal';
const extensionsRegistry = getExtensionsRegistry();
@ -143,7 +142,6 @@ const RightMenu = ({
HAS_GSHEETS_INSTALLED,
} = useSelector<any, ExtentionConfigs>(state => state.common.conf);
const [showDatabaseModal, setShowDatabaseModal] = useState<boolean>(false);
const [showDatasetModal, setShowDatasetModal] = useState<boolean>(false);
const [engine, setEngine] = useState<string>('');
const canSql = findPermission('can_sqllab', 'Superset', roles);
const canDashboard = findPermission('can_write', 'Dashboard', roles);
@ -179,6 +177,7 @@ const RightMenu = ({
{
label: t('Create dataset'),
name: GlobalMenuDataOptions.DATASET_CREATION,
url: '/dataset/add/',
perm: canDataset && nonExamplesDBConnected,
},
{
@ -286,8 +285,6 @@ const RightMenu = ({
} else if (itemChose.key === GlobalMenuDataOptions.GOOGLE_SHEETS) {
setShowDatabaseModal(true);
setEngine('Google Sheets');
} else if (itemChose.key === GlobalMenuDataOptions.DATASET_CREATION) {
setShowDatasetModal(true);
}
};
@ -296,10 +293,6 @@ const RightMenu = ({
setShowDatabaseModal(false);
};
const handleOnHideDatasetModalModal = () => {
setShowDatasetModal(false);
};
const isDisabled = isAdmin && !allowUploads;
const tooltipText = t(
@ -344,7 +337,6 @@ const RightMenu = ({
);
const handleDatabaseAdd = () => setQuery({ databaseAdded: true });
const handleDatasetAdd = () => setQuery({ datasetAdded: true });
const theme = useTheme();
@ -358,13 +350,6 @@ const RightMenu = ({
onDatabaseAdd={handleDatabaseAdd}
/>
)}
{canDataset && (
<AddDatasetModal
onHide={handleOnHideDatasetModalModal}
show={showDatasetModal}
onDatasetAdd={handleDatasetAdd}
/>
)}
{environmentTag?.text && (
<Label
css={{ borderRadius: `${theme.gridUnit * 125}px` }}

View File

@ -228,10 +228,7 @@ class DatasetEditor(BaseSupersetView):
@has_access
@permission_name("read")
def root(self) -> FlaskResponse:
dev = request.args.get("testing")
if dev is not None:
return super().render_app_template()
return redirect("/")
return super().render_app_template()
@expose("/<pk>", methods=["GET"])
@has_access