This is an automated email from the ASF dual-hosted git repository. elizabeth pushed a commit to branch elizabeth/fix-resize-bug in repository https://gitbox.apache.org/repos/asf/superset.git
commit 5845c7c112c8ade8c39573cdf49ab3e04df7a655 Author: Beto Dealmeida <[email protected]> AuthorDate: Tue Jul 29 15:43:47 2025 -0400 feat: allow creating dataset without exploring (#34380) --- .../datasets/AddDataset/Footer/Footer.test.tsx | 146 ++++++++++++++++++++- .../features/datasets/AddDataset/Footer/index.tsx | 52 ++++++-- 2 files changed, 182 insertions(+), 16 deletions(-) diff --git a/superset-frontend/src/features/datasets/AddDataset/Footer/Footer.test.tsx b/superset-frontend/src/features/datasets/AddDataset/Footer/Footer.test.tsx index 65507a170b..fa81ce20e0 100644 --- a/superset-frontend/src/features/datasets/AddDataset/Footer/Footer.test.tsx +++ b/superset-frontend/src/features/datasets/AddDataset/Footer/Footer.test.tsx @@ -16,7 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import { render, screen } from 'spec/helpers/testing-library'; +import { + render, + screen, + waitFor, + userEvent, +} from 'spec/helpers/testing-library'; import Footer from 'src/features/datasets/AddDataset/Footer'; const mockHistoryPush = jest.fn(); @@ -27,6 +32,14 @@ jest.mock('react-router-dom', () => ({ }), })); +// Mock the API call +const mockCreateResource = jest.fn(); +jest.mock('src/views/CRUD/hooks', () => ({ + useSingleViewResource: () => ({ + createResource: mockCreateResource, + }), +})); + const mockedProps = { url: 'realwebsite.com', }; @@ -34,7 +47,7 @@ const mockedProps = { const mockPropsWithDataset = { url: 'realwebsite.com', datasetObject: { - database: { + db: { id: '1', database_name: 'examples', }, @@ -47,6 +60,10 @@ const mockPropsWithDataset = { }; describe('Footer', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + test('renders a Footer with a cancel button and a disabled create button', () => { render(<Footer {...mockedProps} />, { useRedux: true }); @@ -55,21 +72,28 @@ describe('Footer', () => { }); const createButton = screen.getByRole('button', { - name: /Create/i, + name: /Create dataset and create chart/i, }); expect(saveButton).toBeVisible(); expect(createButton).toBeDisabled(); }); - test('renders a Create Dataset button when a table is selected', () => { + test('renders a Create Dataset dropdown button when a table is selected', () => { render(<Footer {...mockPropsWithDataset} />, { useRedux: true }); const createButton = screen.getByRole('button', { - name: /Create/i, + name: /Create dataset and create chart/i, }); expect(createButton).toBeEnabled(); + + // Check that it's a dropdown button with the correct text + expect(createButton).toHaveTextContent('Create dataset and create chart'); + + // Check for the dropdown arrow + const dropdownArrow = screen.getByRole('img', { hidden: true }); + expect(dropdownArrow).toBeInTheDocument(); }); test('create button becomes disabled when table already has a dataset', () => { @@ -78,9 +102,119 @@ describe('Footer', () => { }); const createButton = screen.getByRole('button', { - name: /Create/i, + name: /Create dataset and create chart/i, }); expect(createButton).toBeDisabled(); }); + + test('shows dropdown menu when dropdown arrow is clicked', async () => { + render(<Footer {...mockPropsWithDataset} />, { useRedux: true }); + + // Find and click the dropdown trigger (the arrow part) + const dropdownTrigger = screen.getByRole('button', { name: 'down' }); + userEvent.click(dropdownTrigger); + + // Check that the dropdown menu option is visible + await waitFor(() => { + expect(screen.getByText('Create dataset only')).toBeVisible(); + }); + }); + + test('navigates to chart creation when main button is clicked', async () => { + mockCreateResource.mockResolvedValue(123); // Mock successful dataset creation + + render(<Footer {...mockPropsWithDataset} />, { useRedux: true }); + + const createButton = screen.getByRole('button', { + name: /Create dataset and create chart/i, + }); + + userEvent.click(createButton); + + await waitFor(() => { + expect(mockCreateResource).toHaveBeenCalledWith({ + database: '1', + catalog: undefined, + schema: 'public', + table_name: 'real_info', + }); + expect(mockHistoryPush).toHaveBeenCalledWith( + '/chart/add/?dataset=real_info', + ); + }); + }); + + test('navigates to dataset list when "Create dataset only" menu option is clicked', async () => { + mockCreateResource.mockResolvedValue(123); + + render(<Footer {...mockPropsWithDataset} />, { useRedux: true }); + + // Open dropdown menu + const dropdownTrigger = screen.getByRole('button', { name: 'down' }); + userEvent.click(dropdownTrigger); + + // Click the "Create dataset only" option + await waitFor(() => { + const datasetOnlyOption = screen.getByText('Create dataset only'); + userEvent.click(datasetOnlyOption); + }); + + await waitFor(() => { + expect(mockCreateResource).toHaveBeenCalledWith({ + database: '1', + catalog: undefined, + schema: 'public', + table_name: 'real_info', + }); + expect(mockHistoryPush).toHaveBeenCalledWith('/tablemodelview/list/'); + }); + }); + + test('handles dataset creation failure gracefully', async () => { + mockCreateResource.mockResolvedValue(null); // Mock failed dataset creation + + render(<Footer {...mockPropsWithDataset} />, { useRedux: true }); + + const createButton = screen.getByRole('button', { + name: /Create dataset and create chart/i, + }); + + userEvent.click(createButton); + + await waitFor(() => { + expect(mockCreateResource).toHaveBeenCalled(); + // Should not navigate if creation failed + expect(mockHistoryPush).not.toHaveBeenCalled(); + }); + }); + + test('passes correct data to createResource with catalog', async () => { + const mockPropsWithCatalog = { + ...mockPropsWithDataset, + datasetObject: { + ...mockPropsWithDataset.datasetObject, + catalog: 'test_catalog', + }, + }; + + mockCreateResource.mockResolvedValue(456); + + render(<Footer {...mockPropsWithCatalog} />, { useRedux: true }); + + const createButton = screen.getByRole('button', { + name: /Create dataset and create chart/i, + }); + + userEvent.click(createButton); + + await waitFor(() => { + expect(mockCreateResource).toHaveBeenCalledWith({ + database: '1', + catalog: 'test_catalog', + schema: 'public', + table_name: 'real_info', + }); + }); + }); }); diff --git a/superset-frontend/src/features/datasets/AddDataset/Footer/index.tsx b/superset-frontend/src/features/datasets/AddDataset/Footer/index.tsx index 880e948f57..1a43ff2c52 100644 --- a/superset-frontend/src/features/datasets/AddDataset/Footer/index.tsx +++ b/superset-frontend/src/features/datasets/AddDataset/Footer/index.tsx @@ -17,8 +17,14 @@ * under the License. */ import { useHistory } from 'react-router-dom'; -import { Button } from '@superset-ui/core/components'; -import { t } from '@superset-ui/core'; +import { + Button, + DropdownButton, + Menu, + Flex, +} from '@superset-ui/core/components'; +import { t, useTheme } from '@superset-ui/core'; +import { Icons } from '@superset-ui/core/components/Icons'; import { useSingleViewResource } from 'src/views/CRUD/hooks'; import { logEvent } from 'src/logger/actions'; import withToasts from 'src/components/MessageToasts/withToasts'; @@ -55,6 +61,7 @@ function Footer({ datasets, }: FooterProps) { const history = useHistory(); + const theme = useTheme(); const { createResource } = useSingleViewResource<Partial<DatasetObject>>( 'dataset', t('dataset'), @@ -85,7 +92,7 @@ function Footer({ const tooltipText = t('Select a database table.'); - const onSave = () => { + const onSave = (createChart: boolean = true) => { if (datasetObject) { const data = { database: datasetObject.db?.id, @@ -100,32 +107,57 @@ 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 - history.push(`/chart/add/?dataset=${datasetObject.table_name}`); + if (createChart) { + history.push(`/chart/add/?dataset=${datasetObject.table_name}`); + } else { + history.push('/tablemodelview/list/'); + } } }); } }; + const onSaveOnly = () => { + onSave(false); + }; + const CREATE_DATASET_TEXT = t('Create dataset and create chart'); + const CREATE_DATASET_ONLY_TEXT = t('Create dataset only'); const disabledCheck = !datasetObject?.table_name || !hasColumns || datasets?.includes(datasetObject?.table_name); + const dropdownMenu = ( + <Menu> + <Menu.Item key="create-only" onClick={onSaveOnly}> + {CREATE_DATASET_ONLY_TEXT} + </Menu.Item> + </Menu> + ); + return ( - <> + <Flex align="center" justify="flex-end" gap="8px"> <Button buttonStyle="secondary" onClick={cancelButtonOnClick}> {t('Cancel')} </Button> - <Button - buttonStyle="primary" + <DropdownButton + type="primary" disabled={disabledCheck} tooltip={!datasetObject?.table_name ? tooltipText : undefined} - onClick={onSave} + onClick={() => onSave(true)} + popupRender={() => dropdownMenu} + icon={ + <Icons.DownOutlined + iconSize="xs" + iconColor={theme.colors.grayscale.light5} + /> + } + trigger={['click']} > {CREATE_DATASET_TEXT} - </Button> - </> + </DropdownButton> + </Flex> ); }
