You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ju...@apache.org on 2023/07/20 18:59:37 UTC
[superset] branch master updated: fix(datasets): Replace left panel layout by TableSelector (#24599)
This is an automated email from the ASF dual-hosted git repository.
justinpark pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new b2831b419e fix(datasets): Replace left panel layout by TableSelector (#24599)
b2831b419e is described below
commit b2831b419e1c316cd32b3e0ad29075321460f8bc
Author: JUST.in DO IT <ju...@airbnb.com>
AuthorDate: Thu Jul 20 11:59:31 2023 -0700
fix(datasets): Replace left panel layout by TableSelector (#24599)
Co-authored-by: Justin Park <ju...@apache.org>
---
.../src/components/TableSelector/index.tsx | 8 +-
superset-frontend/src/components/Tooltip/index.tsx | 3 +
.../components/WarningIconWithTooltip/index.tsx | 4 +-
.../AddDataset/LeftPanel/LeftPanel.test.tsx | 286 ++++++++++++---------
.../datasets/AddDataset/LeftPanel/index.tsx | 258 +++----------------
.../datasets/DatasetLayout/DatasetLayout.test.tsx | 2 +-
.../src/features/datasets/hooks/useDatasetLists.ts | 7 +-
7 files changed, 216 insertions(+), 352 deletions(-)
diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx
index 7c2a809e5c..f36056a4a2 100644
--- a/superset-frontend/src/components/TableSelector/index.tsx
+++ b/superset-frontend/src/components/TableSelector/index.tsx
@@ -104,6 +104,7 @@ interface TableSelectorProps {
tableValue?: string | string[];
onTableSelectChange?: (value?: string | string[], schema?: string) => void;
tableSelectMode?: 'single' | 'multiple';
+ customTableOptionLabelRenderer?: (table: Table) => JSX.Element;
}
export interface TableOption {
@@ -132,6 +133,7 @@ export const TableOption = ({ table }: { table: Table }) => {
<WarningIconWithTooltip
warningMarkdown={extra.warning_markdown}
size="l"
+ marginRight={4}
/>
)}
{value}
@@ -164,6 +166,7 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
tableSelectMode = 'single',
tableValue = undefined,
onTableSelectChange,
+ customTableOptionLabelRenderer,
}) => {
const { addSuccessToast } = useToasts();
const [currentSchema, setCurrentSchema] = useState<string | undefined>(
@@ -203,9 +206,12 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
value: table.value,
label: <TableOption table={table} />,
text: table.value,
+ ...(customTableOptionLabelRenderer && {
+ customLabel: customTableOptionLabelRenderer(table),
+ }),
}))
: [],
- [data],
+ [data, customTableOptionLabelRenderer],
);
useEffect(() => {
diff --git a/superset-frontend/src/components/Tooltip/index.tsx b/superset-frontend/src/components/Tooltip/index.tsx
index 64af6b06a0..8237356690 100644
--- a/superset-frontend/src/components/Tooltip/index.tsx
+++ b/superset-frontend/src/components/Tooltip/index.tsx
@@ -41,6 +41,9 @@ export const Tooltip = (props: TooltipProps) => {
display: block;
}
}
+ .ant-tooltip-inner > p {
+ margin: 0;
+ }
`}
/>
<AntdTooltip
diff --git a/superset-frontend/src/components/WarningIconWithTooltip/index.tsx b/superset-frontend/src/components/WarningIconWithTooltip/index.tsx
index f732554e15..82b3ac7e00 100644
--- a/superset-frontend/src/components/WarningIconWithTooltip/index.tsx
+++ b/superset-frontend/src/components/WarningIconWithTooltip/index.tsx
@@ -24,11 +24,13 @@ import { Tooltip } from 'src/components/Tooltip';
export interface WarningIconWithTooltipProps {
warningMarkdown: string;
size?: IconType['iconSize'];
+ marginRight?: number;
}
function WarningIconWithTooltip({
warningMarkdown,
size,
+ marginRight,
}: WarningIconWithTooltipProps) {
const theme = useTheme();
return (
@@ -39,7 +41,7 @@ function WarningIconWithTooltip({
<Icons.AlertSolid
iconColor={theme.colors.alert.base}
iconSize={size}
- css={{ marginRight: theme.gridUnit * 2 }}
+ css={{ marginRight: marginRight ?? theme.gridUnit * 2 }}
/>
</Tooltip>
);
diff --git a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx
index 604e3e9d9e..5156073281 100644
--- a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx
@@ -27,122 +27,128 @@ const databasesEndpoint = 'glob:*/api/v1/database/?q*';
const schemasEndpoint = 'glob:*/api/v1/database/*/schemas*';
const tablesEndpoint = 'glob:*/api/v1/database/*/tables/?q*';
-fetchMock.get(databasesEndpoint, {
- count: 2,
- description_columns: {},
- ids: [1, 2],
- label_columns: {
- allow_file_upload: 'Allow Csv Upload',
- allow_ctas: 'Allow Ctas',
- allow_cvas: 'Allow Cvas',
- allow_dml: 'Allow Dml',
- allow_multi_schema_metadata_fetch: 'Allow Multi Schema Metadata Fetch',
- allow_run_async: 'Allow Run Async',
- allows_cost_estimate: 'Allows Cost Estimate',
- allows_subquery: 'Allows Subquery',
- allows_virtual_table_explore: 'Allows Virtual Table Explore',
- disable_data_preview: 'Disables SQL Lab Data Preview',
- backend: 'Backend',
- changed_on: 'Changed On',
- changed_on_delta_humanized: 'Changed On Delta Humanized',
- 'created_by.first_name': 'Created By First Name',
- 'created_by.last_name': 'Created By Last Name',
- database_name: 'Database Name',
- explore_database_id: 'Explore Database Id',
- expose_in_sqllab: 'Expose In Sqllab',
- force_ctas_schema: 'Force Ctas Schema',
- id: 'Id',
- },
- list_columns: [
- 'allow_file_upload',
- 'allow_ctas',
- 'allow_cvas',
- 'allow_dml',
- 'allow_multi_schema_metadata_fetch',
- 'allow_run_async',
- 'allows_cost_estimate',
- 'allows_subquery',
- 'allows_virtual_table_explore',
- 'disable_data_preview',
- 'backend',
- 'changed_on',
- 'changed_on_delta_humanized',
- 'created_by.first_name',
- 'created_by.last_name',
- 'database_name',
- 'explore_database_id',
- 'expose_in_sqllab',
- 'force_ctas_schema',
- 'id',
- ],
- list_title: 'List Database',
- order_columns: [
- 'allow_file_upload',
- 'allow_dml',
- 'allow_run_async',
- 'changed_on',
- 'changed_on_delta_humanized',
- 'created_by.first_name',
- 'database_name',
- 'expose_in_sqllab',
- ],
- result: [
- {
- allow_file_upload: false,
- allow_ctas: false,
- allow_cvas: false,
- allow_dml: false,
- allow_multi_schema_metadata_fetch: false,
- allow_run_async: false,
- allows_cost_estimate: null,
- allows_subquery: true,
- allows_virtual_table_explore: true,
- disable_data_preview: false,
- backend: 'postgresql',
- changed_on: '2021-03-09T19:02:07.141095',
- changed_on_delta_humanized: 'a day ago',
- created_by: null,
- database_name: 'test-postgres',
- explore_database_id: 1,
- expose_in_sqllab: true,
- force_ctas_schema: null,
- id: 1,
- },
- {
- allow_csv_upload: false,
- allow_ctas: false,
- allow_cvas: false,
- allow_dml: false,
- allow_multi_schema_metadata_fetch: false,
- allow_run_async: false,
- allows_cost_estimate: null,
- allows_subquery: true,
- allows_virtual_table_explore: true,
- disable_data_preview: false,
- backend: 'mysql',
- changed_on: '2021-03-09T19:02:07.141095',
- changed_on_delta_humanized: 'a day ago',
- created_by: null,
- database_name: 'test-mysql',
- explore_database_id: 1,
- expose_in_sqllab: true,
- force_ctas_schema: null,
- id: 2,
+beforeEach(() => {
+ fetchMock.get(databasesEndpoint, {
+ count: 2,
+ description_columns: {},
+ ids: [1, 2],
+ label_columns: {
+ allow_file_upload: 'Allow Csv Upload',
+ allow_ctas: 'Allow Ctas',
+ allow_cvas: 'Allow Cvas',
+ allow_dml: 'Allow Dml',
+ allow_multi_schema_metadata_fetch: 'Allow Multi Schema Metadata Fetch',
+ allow_run_async: 'Allow Run Async',
+ allows_cost_estimate: 'Allows Cost Estimate',
+ allows_subquery: 'Allows Subquery',
+ allows_virtual_table_explore: 'Allows Virtual Table Explore',
+ disable_data_preview: 'Disables SQL Lab Data Preview',
+ backend: 'Backend',
+ changed_on: 'Changed On',
+ changed_on_delta_humanized: 'Changed On Delta Humanized',
+ 'created_by.first_name': 'Created By First Name',
+ 'created_by.last_name': 'Created By Last Name',
+ database_name: 'Database Name',
+ explore_database_id: 'Explore Database Id',
+ expose_in_sqllab: 'Expose In Sqllab',
+ force_ctas_schema: 'Force Ctas Schema',
+ id: 'Id',
},
- ],
-});
+ list_columns: [
+ 'allow_file_upload',
+ 'allow_ctas',
+ 'allow_cvas',
+ 'allow_dml',
+ 'allow_multi_schema_metadata_fetch',
+ 'allow_run_async',
+ 'allows_cost_estimate',
+ 'allows_subquery',
+ 'allows_virtual_table_explore',
+ 'disable_data_preview',
+ 'backend',
+ 'changed_on',
+ 'changed_on_delta_humanized',
+ 'created_by.first_name',
+ 'created_by.last_name',
+ 'database_name',
+ 'explore_database_id',
+ 'expose_in_sqllab',
+ 'force_ctas_schema',
+ 'id',
+ ],
+ list_title: 'List Database',
+ order_columns: [
+ 'allow_file_upload',
+ 'allow_dml',
+ 'allow_run_async',
+ 'changed_on',
+ 'changed_on_delta_humanized',
+ 'created_by.first_name',
+ 'database_name',
+ 'expose_in_sqllab',
+ ],
+ result: [
+ {
+ allow_file_upload: false,
+ allow_ctas: false,
+ allow_cvas: false,
+ allow_dml: false,
+ allow_multi_schema_metadata_fetch: false,
+ allow_run_async: false,
+ allows_cost_estimate: null,
+ allows_subquery: true,
+ allows_virtual_table_explore: true,
+ disable_data_preview: false,
+ backend: 'postgresql',
+ changed_on: '2021-03-09T19:02:07.141095',
+ changed_on_delta_humanized: 'a day ago',
+ created_by: null,
+ database_name: 'test-postgres',
+ explore_database_id: 1,
+ expose_in_sqllab: true,
+ force_ctas_schema: null,
+ id: 1,
+ },
+ {
+ allow_csv_upload: false,
+ allow_ctas: false,
+ allow_cvas: false,
+ allow_dml: false,
+ allow_multi_schema_metadata_fetch: false,
+ allow_run_async: false,
+ allows_cost_estimate: null,
+ allows_subquery: true,
+ allows_virtual_table_explore: true,
+ disable_data_preview: false,
+ backend: 'mysql',
+ changed_on: '2021-03-09T19:02:07.141095',
+ changed_on_delta_humanized: 'a day ago',
+ created_by: null,
+ database_name: 'test-mysql',
+ explore_database_id: 1,
+ expose_in_sqllab: true,
+ force_ctas_schema: null,
+ id: 2,
+ },
+ ],
+ });
+
+ fetchMock.get(schemasEndpoint, {
+ result: ['information_schema', 'public'],
+ });
-fetchMock.get(schemasEndpoint, {
- result: ['information_schema', 'public'],
+ fetchMock.get(tablesEndpoint, {
+ count: 3,
+ result: [
+ { value: 'Sheet1', type: 'table', extra: null },
+ { value: 'Sheet2', type: 'table', extra: null },
+ { value: 'Sheet3', type: 'table', extra: null },
+ ],
+ });
});
-fetchMock.get(tablesEndpoint, {
- count: 3,
- result: [
- { value: 'Sheet1', type: 'table', extra: null },
- { value: 'Sheet2', type: 'table', extra: null },
- { value: 'Sheet3', type: 'table', extra: null },
- ],
+afterEach(() => {
+ fetchMock.reset();
});
const mockFun = jest.fn();
@@ -152,14 +158,16 @@ test('should render', async () => {
useRedux: true,
});
expect(
- await screen.findByText(/select database & schema/i),
+ await screen.findByText(/Select database or type to search databases/i),
).toBeInTheDocument();
});
test('should render schema selector, database selector container, and selects', async () => {
render(<LeftPanel setDataset={mockFun} />, { useRedux: true });
- expect(await screen.findByText(/select database & schema/i)).toBeVisible();
+ expect(
+ await screen.findByText(/Select database or type to search databases/i),
+ ).toBeVisible();
const databaseSelect = screen.getByRole('combobox', {
name: 'Select database or type to search databases',
@@ -175,7 +183,7 @@ test('does not render blank state if there is nothing selected', async () => {
render(<LeftPanel setDataset={mockFun} />, { useRedux: true });
expect(
- await screen.findByText(/select database & schema/i),
+ await screen.findByText(/Select database or type to search databases/i),
).toBeInTheDocument();
const emptyState = screen.queryByRole('img', { name: /empty/i });
expect(emptyState).not.toBeInTheDocument();
@@ -218,25 +226,45 @@ test('searches for a table name', async () => {
const schemaSelect = screen.getByRole('combobox', {
name: /select schema or type to search schemas/i,
});
+ const tableSelect = screen.getByRole('combobox', {
+ name: /select table or type to search tables/i,
+ });
await waitFor(() => expect(schemaSelect).toBeEnabled());
// Click 'public' schema to access tables
userEvent.click(schemaSelect);
userEvent.click(screen.getAllByText('public')[1]);
+ await waitFor(() => expect(fetchMock.calls(tablesEndpoint).length).toBe(1));
+ userEvent.click(tableSelect);
await waitFor(() => {
- expect(screen.getByText('Sheet1')).toBeInTheDocument();
- expect(screen.getByText('Sheet2')).toBeInTheDocument();
- expect(screen.getByText('Sheet3')).toBeInTheDocument();
+ expect(
+ screen.queryByRole('option', {
+ name: /Sheet1/i,
+ }),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByRole('option', {
+ name: /Sheet2/i,
+ }),
+ ).toBeInTheDocument();
});
- userEvent.type(screen.getByRole('textbox'), 'Sheet2');
+ userEvent.type(tableSelect, 'Sheet3');
await waitFor(() => {
- expect(screen.queryByText('Sheet1')).not.toBeInTheDocument();
- expect(screen.getByText('Sheet2')).toBeInTheDocument();
- expect(screen.queryByText('Sheet3')).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole('option', { name: /Sheet1/i }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole('option', { name: /Sheet2/i }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole('option', {
+ name: /Sheet3/i,
+ }),
+ ).toBeInTheDocument();
});
});
@@ -262,6 +290,9 @@ test('renders a warning icon when a table name has a pre-existing dataset', asyn
const schemaSelect = screen.getByRole('combobox', {
name: /select schema or type to search schemas/i,
});
+ const tableSelect = screen.getByRole('combobox', {
+ name: /select table or type to search tables/i,
+ });
await waitFor(() => expect(schemaSelect).toBeEnabled());
@@ -273,11 +304,18 @@ test('renders a warning icon when a table name has a pre-existing dataset', asyn
// Click 'public' schema to access tables
userEvent.click(schemaSelect);
userEvent.click(screen.getAllByText('public')[1]);
+ userEvent.click(tableSelect);
await waitFor(() => {
- expect(screen.getByText('Sheet2')).toBeInTheDocument();
+ expect(
+ screen.queryByRole('option', {
+ name: /Sheet2/i,
+ }),
+ ).toBeInTheDocument();
});
+ userEvent.type(tableSelect, 'Sheet2');
+
// Sheet2 should now show the warning icon
- expect(screen.getByRole('img', { name: 'warning' })).toBeVisible();
+ expect(screen.getByRole('img', { name: 'alert-solid' })).toBeInTheDocument();
});
diff --git a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx
index 90ec555833..715bf2deee 100644
--- a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx
@@ -16,42 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React, {
- useEffect,
- useState,
- SetStateAction,
- Dispatch,
- useCallback,
-} from 'react';
-import rison from 'rison';
-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';
-import { TableOption } from 'src/components/TableSelector';
-import RefreshLabel from 'src/components/RefreshLabel';
-import { Table } from 'src/hooks/apiResources';
-import Loading from 'src/components/Loading';
-import DatabaseSelector, {
- DatabaseObject,
-} from 'src/components/DatabaseSelector';
-import {
- EmptyStateMedium,
- emptyStateComponent,
-} from 'src/components/EmptyState';
+import React, { useEffect, SetStateAction, Dispatch, useCallback } from 'react';
+import { styled, t } from '@superset-ui/core';
+import TableSelector, { TableOption } from 'src/components/TableSelector';
+import { DatabaseObject } from 'src/components/DatabaseSelector';
+import { emptyStateComponent } from 'src/components/EmptyState';
import { useToasts } from 'src/components/MessageToasts/withToasts';
import { LocalStorageKeys, getItem } from 'src/utils/localStorageHelpers';
import {
DatasetActionType,
DatasetObject,
} from 'src/features/datasets/AddDataset/types';
+import { Table } from 'src/hooks/apiResources';
interface LeftPanelProps {
setDataset: Dispatch<SetStateAction<object>>;
@@ -59,10 +35,6 @@ interface LeftPanelProps {
datasetNames?: (string | null | undefined)[] | undefined;
}
-const SearchIcon = styled(Icons.Search)`
- color: ${({ theme }) => theme.colors.grayscale.light1};
-`;
-
const LeftPanelStyle = styled.div`
${({ theme }) => `
max-width: ${theme.gridUnit * 87.5}px;
@@ -74,14 +46,6 @@ const LeftPanelStyle = styled.div`
height: auto;
margin-top: ${theme.gridUnit * 17.5}px;
}
- .refresh {
- position: absolute;
- top: ${theme.gridUnit * 38.75}px;
- left: ${theme.gridUnit * 16.75}px;
- span[role="button"]{
- font-size: ${theme.gridUnit * 4.25}px;
- }
- }
.section-title {
margin-top: ${theme.gridUnit * 5.5}px;
margin-bottom: ${theme.gridUnit * 11}px;
@@ -158,77 +122,28 @@ export default function LeftPanel({
dataset,
datasetNames,
}: LeftPanelProps) {
- const theme = useTheme();
-
- const [tableOptions, setTableOptions] = useState<Array<TableOption>>([]);
- const [resetTables, setResetTables] = useState(false);
- const [loadTables, setLoadTables] = useState(false);
- const [searchVal, setSearchVal] = useState('');
- const [refresh, setRefresh] = useState(false);
- const [selectedTable, setSelectedTable] = useState<number | null>(null);
-
const { addDangerToast } = useToasts();
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);
- setDataset({
- type: DatasetActionType.selectTable,
- payload: { name: 'table_name', value: tableName },
- });
- };
-
- const getTablesList = useCallback(
- (url: string) => {
- SupersetClient.get({ url })
- .then(({ json }) => {
- const options: TableOption[] = json.result.map((table: Table) => {
- const option: TableOption = {
- value: table.value,
- label: <TableOption table={table} />,
- text: table.label,
- };
-
- 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);
- });
- },
- [addDangerToast],
- );
-
const setSchema = (schema: string) => {
if (schema) {
setDataset({
type: DatasetActionType.selectSchema,
payload: { name: 'schema', value: schema },
});
- setLoadTables(true);
}
- setSelectedTable(null);
- setResetTables(true);
};
-
- const encodedSchema = dataset?.schema
- ? encodeURIComponent(dataset?.schema)
- : undefined;
-
+ const setTable = (tableName: string) => {
+ setDataset({
+ type: DatasetActionType.selectTable,
+ payload: { name: 'table_name', value: tableName },
+ });
+ };
useEffect(() => {
const currentUserSelectedDb = getItem(
LocalStorageKeys.db,
@@ -239,140 +154,37 @@ export default function LeftPanel({
}
}, [setDatabase]);
- useEffect(() => {
- if (loadTables) {
- const params = rison.encode({
- force: refresh,
- schema_name: encodedSchema,
- });
-
- const endpoint = `/api/v1/database/${dataset?.db?.id}/tables/?q=${params}`;
- getTablesList(endpoint);
- }
- }, [loadTables, dataset?.db?.id, encodedSchema, getTablesList, refresh]);
-
- useEffect(() => {
- if (resetTables) {
- setTableOptions([]);
- setResetTables(false);
- }
- }, [resetTables]);
-
- const filteredOptions = tableOptions.filter(option =>
- option?.value?.toLowerCase().includes(searchVal.toLowerCase()),
- );
-
- const Loader = (inline: string) => (
- <div className="loading-container">
- <Loading position="inline" />
- <p>{inline}</p>
- </div>
+ const customTableOptionLabelRenderer = useCallback(
+ (table: Table) => (
+ <TableOption
+ table={
+ datasetNames?.includes(table.value)
+ ? {
+ ...table,
+ extra: {
+ warning_markdown: t('This table already has a dataset'),
+ },
+ }
+ : table
+ }
+ />
+ ),
+ [datasetNames],
);
- 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');
-
- const optionsList = document.getElementsByClassName('options-list');
- const scrollableOptionsList =
- optionsList[0]?.scrollHeight > optionsList[0]?.clientHeight;
- const [emptyResultsWithSearch, setEmptyResultsWithSearch] = useState(false);
-
- const onEmptyResults = (searchText?: string) => {
- setEmptyResultsWithSearch(!!searchText);
- };
-
return (
<LeftPanelStyle>
- <p className="section-title db-schema">
- {SELECT_DATABASE_AND_SCHEMA_TEXT}
- </p>
- <DatabaseSelector
- db={dataset?.db}
+ <TableSelector
+ database={dataset?.db}
handleError={addDangerToast}
+ emptyState={emptyStateComponent(false)}
onDbChange={setDatabase}
onSchemaChange={setSchema}
- emptyState={emptyStateComponent(emptyResultsWithSearch)}
- onEmptyResults={onEmptyResults}
+ onTableSelectChange={setTable}
+ sqlLabMode={false}
+ customTableOptionLabelRenderer={customTableOptionLabelRenderer}
+ {...(dataset?.schema && { schema: dataset.schema })}
/>
- {loadTables && !refresh && Loader(TABLE_LOADING_TEXT)}
- {dataset?.schema && !loadTables && !tableOptions.length && !searchVal && (
- <div className="emptystate">
- <EmptyStateMedium
- image="empty-table.svg"
- title={NO_TABLES_FOUND_TITLE}
- description={NO_TABLES_FOUND_DESCRIPTION}
- />
- </div>
- )}
-
- {dataset?.schema && (tableOptions.length > 0 || searchVal.length > 0) && (
- <>
- <Form>
- <p className="table-title">{SELECT_DATABASE_TABLE_TEXT}</p>
- <RefreshLabel
- onClick={() => {
- setLoadTables(true);
- setRefresh(true);
- }}
- tooltipContent={REFRESH_TABLE_LIST_TOOLTIP}
- />
- {refresh && Loader(REFRESH_TABLES_TEXT)}
- {!refresh && (
- <Input
- value={searchVal}
- prefix={<SearchIcon iconSize="l" />}
- onChange={evt => {
- setSearchVal(evt.target.value);
- }}
- className="table-form"
- placeholder={SEARCH_TABLES_PLACEHOLDER_TEXT}
- allowClear
- />
- )}
- </Form>
- <div className="options-list" data-test="options-list">
- {!refresh &&
- filteredOptions.map((option, i) => (
- <div
- className={
- selectedTable === i
- ? scrollableOptionsList
- ? 'options-highlighted'
- : 'options-highlighted no-scrollbar'
- : scrollableOptionsList
- ? 'options'
- : 'options no-scrollbar'
- }
- key={i}
- role="button"
- tabIndex={0}
- onClick={() => setTable(option.value, i)}
- >
- {option.label}
- {datasetNames?.includes(option.value) && (
- <Icons.Warning
- iconColor={
- selectedTable === i
- ? theme.colors.grayscale.light5
- : theme.colors.info.base
- }
- iconSize="m"
- css={css`
- margin-right: ${theme.gridUnit * 2}px;
- `}
- />
- )}
- </div>
- ))}
- </div>
- </>
- )}
</LeftPanelStyle>
);
}
diff --git a/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx b/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx
index a851b2b3bc..66cbf6f0c4 100644
--- a/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx
+++ b/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx
@@ -59,7 +59,7 @@ describe('DatasetLayout', () => {
);
expect(
- await screen.findByText(/select database & schema/i),
+ await screen.findByText(/Select database or type to search databases/i),
).toBeInTheDocument();
expect(LeftPanel).toBeTruthy();
});
diff --git a/superset-frontend/src/features/datasets/hooks/useDatasetLists.ts b/superset-frontend/src/features/datasets/hooks/useDatasetLists.ts
index 1c4b00df2d..373d98946f 100644
--- a/superset-frontend/src/features/datasets/hooks/useDatasetLists.ts
+++ b/superset-frontend/src/features/datasets/hooks/useDatasetLists.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useState, useEffect, useCallback } from 'react';
+import { useState, useEffect, useCallback, useMemo } from 'react';
import { SupersetClient, logging, t } from '@superset-ui/core';
import rison from 'rison';
import { addDangerToast } from 'src/components/MessageToasts/actions';
@@ -83,7 +83,10 @@ const useDatasetsList = (
}
}, [db?.id, schema, encodedSchema, getDatasetsList]);
- const datasetNames = datasets?.map(dataset => dataset.table_name);
+ const datasetNames = useMemo(
+ () => datasets?.map(dataset => dataset.table_name),
+ [datasets],
+ );
return { datasets, datasetNames };
};