chore: Use queryEditorId in SqlEditor child components (#21650)

This commit is contained in:
EugeneTorap
2022-11-16 16:06:20 +03:00
committed by GitHub
parent 2f0d5f16f3
commit d76f305343
17 changed files with 240 additions and 254 deletions

View File

@@ -19,23 +19,17 @@
import React from 'react';
import configureStore from 'redux-mock-store';
import fetchMock from 'fetch-mock';
import { render, screen, act } from 'spec/helpers/testing-library';
import { render, screen, waitFor } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { Provider } from 'react-redux';
import '@testing-library/jest-dom/extend-expect';
import thunk from 'redux-thunk';
import SqlEditorLeftBar from 'src/SqlLab/components/SqlEditorLeftBar';
import {
table,
initialState,
defaultQueryEditor,
mockedActions,
} from 'src/SqlLab/fixtures';
import { table, initialState, defaultQueryEditor } from 'src/SqlLab/fixtures';
const mockedProps = {
actions: mockedActions,
tables: [table],
queryEditor: defaultQueryEditor,
queryEditorId: defaultQueryEditor.id,
database: {
id: 1,
database_name: 'main',
@@ -58,103 +52,91 @@ fetchMock.get('glob:*/superset/tables/**', {
tableLength: 1,
});
describe('Left Panel Expansion', () => {
test('is valid', () => {
const renderAndWait = (props, store) =>
waitFor(() =>
render(<SqlEditorLeftBar {...props} />, {
useRedux: true,
...(store && { store }),
}),
);
test('is valid', () => {
expect(
React.isValidElement(
<Provider store={store}>
<SqlEditorLeftBar {...mockedProps} />
</Provider>,
),
).toBe(true);
});
test('renders a TableElement', async () => {
await renderAndWait(mockedProps, store);
expect(await screen.findByText(/Database/i)).toBeInTheDocument();
const tableElement = screen.getAllByTestId('table-element');
expect(tableElement.length).toBeGreaterThanOrEqual(1);
});
test('table should be visible when expanded is true', async () => {
const { container } = await renderAndWait(mockedProps, store);
const dbSelect = screen.getByRole('combobox', {
name: 'Select database or type database name',
});
const schemaSelect = screen.getByRole('combobox', {
name: 'Select schema or type schema name',
});
const dropdown = screen.getByText(/Table/i);
const abUser = screen.queryAllByText(/ab_user/i);
await waitFor(() => {
expect(screen.getByText(/Database/i)).toBeInTheDocument();
expect(dbSelect).toBeInTheDocument();
expect(schemaSelect).toBeInTheDocument();
expect(dropdown).toBeInTheDocument();
expect(abUser).toHaveLength(2);
expect(
React.isValidElement(
<Provider store={store}>
<SqlEditorLeftBar {...mockedProps} />
</Provider>,
),
).toBe(true);
});
test('renders a TableElement', async () => {
render(<SqlEditorLeftBar {...mockedProps} />, {
useRedux: true,
initialState,
});
expect(await screen.findByText(/Database/i)).toBeInTheDocument();
expect(
screen.queryAllByTestId('table-element').length,
).toBeGreaterThanOrEqual(1);
});
test('table should be visible when expanded is true', async () => {
const { container } = render(<SqlEditorLeftBar {...mockedProps} />, {
useRedux: true,
initialState,
});
const dbSelect = screen.getByRole('combobox', {
name: 'Select database or type database name',
});
const schemaSelect = screen.getByRole('combobox', {
name: 'Select schema or type schema name',
});
const dropdown = screen.getByText(/Table/i);
const abUser = screen.queryAllByText(/ab_user/i);
act(async () => {
expect(await screen.findByText(/Database/i)).toBeInTheDocument();
expect(dbSelect).toBeInTheDocument();
expect(schemaSelect).toBeInTheDocument();
expect(dropdown).toBeInTheDocument();
expect(abUser).toHaveLength(2);
expect(
container.querySelector('.ant-collapse-content-active'),
).toBeInTheDocument();
});
});
test('should toggle the table when the header is clicked', async () => {
const collapseMock = jest.fn();
render(
<SqlEditorLeftBar
{...mockedProps}
actions={{ ...mockedActions, collapseTable: collapseMock }}
/>,
{
useRedux: true,
initialState,
},
);
expect(await screen.findByText(/ab_user/)).toBeInTheDocument();
const header = screen.getByText(/ab_user/);
userEvent.click(header);
expect(collapseMock).toHaveBeenCalled();
});
test('When changing database the table list must be updated', async () => {
const { rerender } = render(<SqlEditorLeftBar {...mockedProps} />, {
useRedux: true,
initialState,
});
await act(async () => {
expect(await screen.findByText(/main/i)).toBeInTheDocument();
expect(await screen.findByText(/ab_user/i)).toBeInTheDocument();
});
rerender(
<SqlEditorLeftBar
{...mockedProps}
actions={{ ...mockedActions }}
database={{
id: 2,
database_name: 'new_db',
backend: 'postgresql',
}}
queryEditor={{ ...mockedProps.queryEditor, schema: 'new_schema' }}
tables={[{ ...mockedProps.tables[0], dbId: 2, name: 'new_table' }]}
/>,
{
useRedux: true,
initialState,
},
);
expect(await screen.findByText(/new_db/i)).toBeInTheDocument();
expect(await screen.findByText(/new_table/i)).toBeInTheDocument();
container.querySelector('.ant-collapse-content-active'),
).toBeInTheDocument();
});
});
test('should toggle the table when the header is clicked', async () => {
const store = mockStore(initialState);
await renderAndWait(mockedProps, store);
const header = (await screen.findAllByText(/ab_user/))[1];
expect(header).toBeInTheDocument();
userEvent.click(header);
await waitFor(() => {
expect(store.getActions()).toHaveLength(2);
expect(store.getActions()[1].type).toEqual('COLLAPSE_TABLE');
});
});
test('When changing database the table list must be updated', async () => {
const { rerender } = await renderAndWait(mockedProps, store);
expect(screen.getAllByText(/main/i)[0]).toBeInTheDocument();
expect(screen.getAllByText(/ab_user/i)[0]).toBeInTheDocument();
rerender(
<SqlEditorLeftBar
{...mockedProps}
database={{
id: 2,
database_name: 'new_db',
backend: 'postgresql',
}}
queryEditor={{ ...mockedProps.queryEditor, schema: 'new_schema' }}
tables={[{ ...mockedProps.tables[0], dbId: 2, name: 'new_table' }]}
/>,
{
useRedux: true,
initialState,
},
);
expect(await screen.findByText(/new_db/i)).toBeInTheDocument();
expect(await screen.findByText(/new_table/i)).toBeInTheDocument();
});

View File

@@ -18,21 +18,35 @@
*/
import React, {
useEffect,
useRef,
useCallback,
useMemo,
useState,
Dispatch,
SetStateAction,
} from 'react';
import { useDispatch } from 'react-redux';
import querystring from 'query-string';
import {
queryEditorSetDb,
queryEditorSetFunctionNames,
addTable,
removeTables,
collapseTable,
expandTable,
queryEditorSetSchema,
queryEditorSetTableOptions,
queryEditorSetSchemaOptions,
setDatabases,
addDangerToast,
resetState,
} from 'src/SqlLab/actions/sqlLab';
import Button from 'src/components/Button';
import { t, styled, css, SupersetTheme } from '@superset-ui/core';
import Collapse from 'src/components/Collapse';
import Icons from 'src/components/Icons';
import { TableSelectorMultiple } from 'src/components/TableSelector';
import { IconTooltip } from 'src/components/IconTooltip';
import { QueryEditor, SchemaOption } from 'src/SqlLab/types';
import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
import { DatabaseObject } from 'src/components/DatabaseSelector';
import { EmptyStateSmall } from 'src/components/EmptyState';
@@ -41,37 +55,16 @@ import {
LocalStorageKeys,
setItem,
} from 'src/utils/localStorageHelpers';
import TableElement, { Table, TableElementProps } from '../TableElement';
import TableElement, { Table } from '../TableElement';
interface ExtendedTable extends Table {
expanded: boolean;
}
interface actionsTypes {
queryEditorSetDb: (queryEditor: QueryEditor, dbId: number) => void;
queryEditorSetFunctionNames: (queryEditor: QueryEditor, dbId: number) => void;
collapseTable: (table: Table) => void;
expandTable: (table: Table) => void;
addTable: (queryEditor: any, database: any, value: any, schema: any) => void;
setDatabases: (arg0: any) => {};
addDangerToast: (msg: string) => void;
queryEditorSetSchema: (queryEditor: QueryEditor, schema?: string) => void;
queryEditorSetSchemaOptions: (
queryEditor: QueryEditor,
options: SchemaOption[],
) => void;
queryEditorSetTableOptions: (
queryEditor: QueryEditor,
options: Array<any>,
) => void;
resetState: () => void;
}
interface SqlEditorLeftBarProps {
queryEditor: QueryEditor;
queryEditorId: string;
height?: number;
tables?: ExtendedTable[];
actions: actionsTypes & TableElementProps['actions'];
database: DatabaseObject;
setEmptyState: Dispatch<SetStateAction<boolean>>;
}
@@ -102,22 +95,21 @@ const collapseStyles = (theme: SupersetTheme) => css`
}
`;
export default function SqlEditorLeftBar({
actions,
const SqlEditorLeftBar = ({
database,
queryEditor,
queryEditorId,
tables = [],
height = 500,
setEmptyState,
}: SqlEditorLeftBarProps) {
// Ref needed to avoid infinite rerenders on handlers
// that require and modify the queryEditor
const queryEditorRef = useRef<QueryEditor>(queryEditor);
}: SqlEditorLeftBarProps) => {
const dispatch = useDispatch();
const queryEditor = useQueryEditor(queryEditorId, ['dbId', 'schema']);
const [emptyResultsWithSearch, setEmptyResultsWithSearch] = useState(false);
const [userSelectedDb, setUserSelected] = useState<DatabaseObject | null>(
null,
);
const { schema } = useQueryEditor(queryEditor.id, ['schema']);
const { schema } = queryEditor;
useEffect(() => {
const bool = querystring.parse(window.location.search).db;
@@ -132,18 +124,14 @@ export default function SqlEditorLeftBar({
} else setUserSelected(database);
}, [database]);
useEffect(() => {
queryEditorRef.current = queryEditor;
}, [queryEditor, database]);
const onEmptyResults = (searchText?: string) => {
setEmptyResultsWithSearch(!!searchText);
};
const onDbChange = ({ id: dbId }: { id: number }) => {
setEmptyState(false);
actions.queryEditorSetDb(queryEditor, dbId);
actions.queryEditorSetFunctionNames(queryEditor, dbId);
dispatch(queryEditorSetDb(queryEditor, dbId));
dispatch(queryEditorSetFunctionNames(queryEditor, dbId));
};
const selectedTableNames = useMemo(
@@ -168,21 +156,21 @@ export default function SqlEditorLeftBar({
});
tablesToAdd.forEach(tableName =>
actions.addTable(queryEditor, database, tableName, schemaName),
dispatch(addTable(queryEditor, database, tableName, schemaName)),
);
actions.removeTables(currentTables);
dispatch(removeTables(currentTables));
};
const onToggleTable = (updatedTables: string[]) => {
tables.forEach((table: ExtendedTable) => {
if (!updatedTables.includes(table.id.toString()) && table.expanded) {
actions.collapseTable(table);
dispatch(collapseTable(table));
} else if (
updatedTables.includes(table.id.toString()) &&
!table.expanded
) {
actions.expandTable(table);
dispatch(expandTable(table));
}
});
};
@@ -225,41 +213,45 @@ export default function SqlEditorLeftBar({
}
/>
);
const handleSchemaChange = useCallback(
(schema: string) => {
if (queryEditorRef.current) {
actions.queryEditorSetSchema(queryEditorRef.current, schema);
}
},
[actions],
);
const handleTablesLoad = React.useCallback(
(options: Array<any>) => {
if (queryEditorRef.current) {
actions.queryEditorSetTableOptions(queryEditorRef.current, options);
}
},
[actions],
);
const handleSchemaChange = useCallback((schema: string) => {
if (queryEditor) {
dispatch(queryEditorSetSchema(queryEditor, schema));
}
}, []);
const handleSchemasLoad = React.useCallback(
(options: Array<any>) => {
if (queryEditorRef.current) {
actions.queryEditorSetSchemaOptions(queryEditorRef.current, options);
}
},
[actions],
);
const handleTablesLoad = useCallback((options: Array<any>) => {
if (queryEditor) {
dispatch(queryEditorSetTableOptions(queryEditor, options));
}
}, []);
const handleSchemasLoad = useCallback((options: Array<any>) => {
if (queryEditor) {
dispatch(queryEditorSetSchemaOptions(queryEditor, options));
}
}, []);
const handleDbList = useCallback((result: DatabaseObject) => {
dispatch(setDatabases(result));
}, []);
const handleError = useCallback((message: string) => {
dispatch(addDangerToast(message));
}, []);
const handleResetState = useCallback(() => {
dispatch(resetState());
}, []);
return (
<div className="SqlEditorLeftBar">
<div data-test="sql-editor-left-bar" className="SqlEditorLeftBar">
<TableSelectorMultiple
onEmptyResults={onEmptyResults}
emptyState={emptyStateComponent}
database={userSelectedDb}
getDbList={actions.setDatabases}
handleError={actions.addDangerToast}
getDbList={handleDbList}
handleError={handleError}
onDbChange={onDbChange}
onSchemaChange={handleSchemaChange}
onSchemasLoad={handleSchemasLoad}
@@ -287,7 +279,7 @@ export default function SqlEditorLeftBar({
expandIcon={renderExpandIconWithTooltip}
>
{tables.map(table => (
<TableElement table={table} key={table.id} actions={actions} />
<TableElement table={table} key={table.id} />
))}
</Collapse>
</div>
@@ -296,11 +288,13 @@ export default function SqlEditorLeftBar({
<Button
buttonSize="small"
buttonStyle="danger"
onClick={actions.resetState}
onClick={handleResetState}
>
<i className="fa fa-bomb" /> {t('Reset state')}
</Button>
)}
</div>
);
}
};
export default SqlEditorLeftBar;