mirror of https://github.com/apache/superset.git
fix: A newly connected database doesn't appear in the databases list if user connected database using the 'plus' button (#19967)
* fix: A newly connected database doesn't appear in the databases list if user connected database using the 'plus' button * include onDatabaseAdd on successful import
This commit is contained in:
parent
86368dd406
commit
8345eb4644
|
@ -20,6 +20,8 @@ import { SupersetClient, t, styled } from '@superset-ui/core';
|
|||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import rison from 'rison';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useQueryParams, BooleanParam } from 'use-query-params';
|
||||
|
||||
import Loading from 'src/components/Loading';
|
||||
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
|
||||
import { useListViewResource } from 'src/views/CRUD/hooks';
|
||||
|
@ -91,6 +93,10 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
|
|||
state => state.user,
|
||||
);
|
||||
|
||||
const [query, setQuery] = useQueryParams({
|
||||
databaseAdded: BooleanParam,
|
||||
});
|
||||
|
||||
const [databaseModalOpen, setDatabaseModalOpen] = useState<boolean>(false);
|
||||
const [databaseCurrentlyDeleting, setDatabaseCurrentlyDeleting] =
|
||||
useState<DatabaseDeleteObject | null>(null);
|
||||
|
@ -110,6 +116,13 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
|
|||
ALLOWED_EXTENSIONS,
|
||||
} = useSelector<any, ExtentionConfigs>(state => state.common.conf);
|
||||
|
||||
useEffect(() => {
|
||||
if (query?.databaseAdded) {
|
||||
setQuery({ databaseAdded: undefined });
|
||||
refreshData();
|
||||
}
|
||||
}, [query, setQuery, refreshData]);
|
||||
|
||||
const openDatabaseDeleteModal = (database: DatabaseObject) =>
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/database/${database.id}/related_objects/`,
|
||||
|
|
|
@ -519,7 +519,6 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
|||
setImportingModal(false);
|
||||
setPasswords({});
|
||||
setConfirmedOverwrite(false);
|
||||
if (onDatabaseAdd) onDatabaseAdd();
|
||||
onHide();
|
||||
};
|
||||
|
||||
|
@ -652,6 +651,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
|||
confirmedOverwrite,
|
||||
);
|
||||
if (dbId) {
|
||||
if (onDatabaseAdd) onDatabaseAdd();
|
||||
onClose();
|
||||
addSuccessToast(t('Database connected'));
|
||||
}
|
||||
|
|
|
@ -248,13 +248,16 @@ beforeEach(() => {
|
|||
|
||||
test('should render', () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
const { container } = render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
const { container } = render(<Menu {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
useQueryParams: true,
|
||||
});
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the navigation', () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
expect(screen.getByRole('navigation')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
@ -265,7 +268,7 @@ test('should render the brand', () => {
|
|||
brand: { alt, icon },
|
||||
},
|
||||
} = mockedProps;
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
const image = screen.getByAltText(alt);
|
||||
expect(image).toHaveAttribute('src', icon);
|
||||
});
|
||||
|
@ -275,7 +278,7 @@ test('should render all the top navbar menu items', () => {
|
|||
const {
|
||||
data: { menu },
|
||||
} = mockedProps;
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
menu.forEach(item => {
|
||||
expect(screen.getByText(item.label)).toBeInTheDocument();
|
||||
});
|
||||
|
@ -286,7 +289,7 @@ test('should render the top navbar child menu items', async () => {
|
|||
const {
|
||||
data: { menu },
|
||||
} = mockedProps;
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
const sources = screen.getByText('Sources');
|
||||
userEvent.hover(sources);
|
||||
const datasets = await screen.findByText('Datasets');
|
||||
|
@ -300,7 +303,7 @@ test('should render the top navbar child menu items', async () => {
|
|||
|
||||
test('should render the dropdown items', async () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...notanonProps} />, { useRedux: true });
|
||||
render(<Menu {...notanonProps} />, { useRedux: true, useQueryParams: true });
|
||||
const dropdown = screen.getByTestId('new-dropdown-icon');
|
||||
userEvent.hover(dropdown);
|
||||
// todo (philip): test data submenu
|
||||
|
@ -326,14 +329,14 @@ test('should render the dropdown items', async () => {
|
|||
|
||||
test('should render the Settings', async () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
const settings = await screen.findByText('Settings');
|
||||
expect(settings).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the Settings menu item', async () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
userEvent.hover(screen.getByText('Settings'));
|
||||
const label = await screen.findByText('Security');
|
||||
expect(label).toBeInTheDocument();
|
||||
|
@ -344,7 +347,7 @@ test('should render the Settings dropdown child menu items', async () => {
|
|||
const {
|
||||
data: { settings },
|
||||
} = mockedProps;
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
userEvent.hover(screen.getByText('Settings'));
|
||||
const listUsers = await screen.findByText('List Users');
|
||||
expect(listUsers).toHaveAttribute('href', settings[0].childs[0].url);
|
||||
|
@ -352,13 +355,13 @@ test('should render the Settings dropdown child menu items', async () => {
|
|||
|
||||
test('should render the plus menu (+) when user is not anonymous', () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...notanonProps} />, { useRedux: true });
|
||||
render(<Menu {...notanonProps} />, { useRedux: true, useQueryParams: true });
|
||||
expect(screen.getByTestId('new-dropdown')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should NOT render the plus menu (+) when user is anonymous', () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
expect(screen.queryByTestId('new-dropdown')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
@ -370,7 +373,7 @@ test('should render the user actions when user is not anonymous', async () => {
|
|||
},
|
||||
} = mockedProps;
|
||||
|
||||
render(<Menu {...notanonProps} />, { useRedux: true });
|
||||
render(<Menu {...notanonProps} />, { useRedux: true, useQueryParams: true });
|
||||
userEvent.hover(screen.getByText('Settings'));
|
||||
const user = await screen.findByText('User');
|
||||
expect(user).toBeInTheDocument();
|
||||
|
@ -384,7 +387,7 @@ test('should render the user actions when user is not anonymous', async () => {
|
|||
|
||||
test('should NOT render the user actions when user is anonymous', () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
expect(screen.queryByText('User')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
@ -396,7 +399,7 @@ test('should render the Profile link when available', async () => {
|
|||
},
|
||||
} = mockedProps;
|
||||
|
||||
render(<Menu {...notanonProps} />, { useRedux: true });
|
||||
render(<Menu {...notanonProps} />, { useRedux: true, useQueryParams: true });
|
||||
|
||||
userEvent.hover(screen.getByText('Settings'));
|
||||
const profile = await screen.findByText('Profile');
|
||||
|
@ -411,7 +414,7 @@ test('should render the About section and version_string, sha or build_number wh
|
|||
},
|
||||
} = mockedProps;
|
||||
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
userEvent.hover(screen.getByText('Settings'));
|
||||
const about = await screen.findByText('About');
|
||||
const version = await screen.findByText(`Version: ${version_string}`);
|
||||
|
@ -430,7 +433,7 @@ test('should render the Documentation link when available', async () => {
|
|||
navbar_right: { documentation_url },
|
||||
},
|
||||
} = mockedProps;
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
userEvent.hover(screen.getByText('Settings'));
|
||||
const doc = await screen.findByTitle('Documentation');
|
||||
expect(doc).toHaveAttribute('href', documentation_url);
|
||||
|
@ -444,7 +447,7 @@ test('should render the Bug Report link when available', async () => {
|
|||
},
|
||||
} = mockedProps;
|
||||
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
const bugReport = await screen.findByTitle('Report a bug');
|
||||
expect(bugReport).toHaveAttribute('href', bug_report_url);
|
||||
});
|
||||
|
@ -457,19 +460,19 @@ test('should render the Login link when user is anonymous', () => {
|
|||
},
|
||||
} = mockedProps;
|
||||
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
const login = screen.getByText('Login');
|
||||
expect(login).toHaveAttribute('href', user_login_url);
|
||||
});
|
||||
|
||||
test('should render the Language Picker', () => {
|
||||
useSelectorMock.mockReturnValue({ roles: user.roles });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
expect(screen.getByLabelText('Languages')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should hide create button without proper roles', () => {
|
||||
useSelectorMock.mockReturnValue({ roles: [] });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true });
|
||||
render(<Menu {...mockedProps} />, { useRedux: true, useQueryParams: true });
|
||||
expect(screen.queryByTestId('new-dropdown')).not.toBeInTheDocument();
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ import React, { Fragment, useState, useEffect } from 'react';
|
|||
import rison from 'rison';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useQueryParams, BooleanParam } from 'use-query-params';
|
||||
|
||||
import {
|
||||
t,
|
||||
styled,
|
||||
|
@ -94,6 +96,10 @@ const RightMenu = ({
|
|||
state => state.dashboardInfo?.id,
|
||||
);
|
||||
|
||||
const [, setQuery] = useQueryParams({
|
||||
databaseAdded: BooleanParam,
|
||||
});
|
||||
|
||||
const { roles } = user;
|
||||
const {
|
||||
CSV_EXTENSIONS,
|
||||
|
@ -250,6 +256,8 @@ const RightMenu = ({
|
|||
return null;
|
||||
};
|
||||
|
||||
const handleDatabaseAdd = () => setQuery({ databaseAdded: true });
|
||||
|
||||
return (
|
||||
<StyledDiv align={align}>
|
||||
{canDatabase && (
|
||||
|
@ -257,6 +265,7 @@ const RightMenu = ({
|
|||
onHide={handleOnHideModal}
|
||||
show={showModal}
|
||||
dbEngine={engine}
|
||||
onDatabaseAdd={handleDatabaseAdd}
|
||||
/>
|
||||
)}
|
||||
<Menu
|
||||
|
|
Loading…
Reference in New Issue