mirror of
https://github.com/apache/superset.git
synced 2026-04-25 11:04:48 +00:00
refactor: Changes the DatabaseSelector and TableSelector to use the new Select component (#16483)
This commit is contained in:
committed by
GitHub
parent
1d5100daa2
commit
596e1cdf9b
@@ -26,11 +26,11 @@ import DatabaseSelector from '.';
|
||||
const SupersetClientGet = jest.spyOn(SupersetClient, 'get');
|
||||
|
||||
const createProps = () => ({
|
||||
dbId: 1,
|
||||
db: { id: 1, database_name: 'test', backend: 'test-postgresql' },
|
||||
formMode: false,
|
||||
isDatabaseSelectEnabled: true,
|
||||
readOnly: false,
|
||||
schema: 'public',
|
||||
schema: undefined,
|
||||
sqlLabMode: true,
|
||||
getDbList: jest.fn(),
|
||||
getTableList: jest.fn(),
|
||||
@@ -57,9 +57,9 @@ beforeEach(() => {
|
||||
}
|
||||
return {
|
||||
json: {
|
||||
count: 1,
|
||||
count: 2,
|
||||
description_columns: {},
|
||||
ids: [1],
|
||||
ids: [1, 2],
|
||||
label_columns: {
|
||||
allow_csv_upload: 'Allow Csv Upload',
|
||||
allow_ctas: 'Allow Ctas',
|
||||
@@ -129,12 +129,32 @@ beforeEach(() => {
|
||||
changed_on: '2021-03-09T19:02:07.141095',
|
||||
changed_on_delta_humanized: 'a day ago',
|
||||
created_by: null,
|
||||
database_name: 'examples',
|
||||
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,
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
@@ -153,50 +173,95 @@ test('Refresh should work', async () => {
|
||||
|
||||
render(<DatabaseSelector {...props} />);
|
||||
|
||||
const select = screen.getByRole('combobox', {
|
||||
name: 'Select schema or type schema name',
|
||||
});
|
||||
|
||||
userEvent.click(select);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(SupersetClientGet).toBeCalledTimes(2);
|
||||
expect(props.getDbList).toBeCalledTimes(1);
|
||||
expect(SupersetClientGet).toBeCalledTimes(1);
|
||||
expect(props.getDbList).toBeCalledTimes(0);
|
||||
expect(props.getTableList).toBeCalledTimes(0);
|
||||
expect(props.handleError).toBeCalledTimes(0);
|
||||
expect(props.onDbChange).toBeCalledTimes(0);
|
||||
expect(props.onSchemaChange).toBeCalledTimes(0);
|
||||
expect(props.onSchemasLoad).toBeCalledTimes(1);
|
||||
expect(props.onSchemasLoad).toBeCalledTimes(0);
|
||||
expect(props.onUpdate).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
userEvent.click(screen.getByRole('button'));
|
||||
userEvent.click(screen.getByRole('button', { name: 'refresh' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(SupersetClientGet).toBeCalledTimes(3);
|
||||
expect(props.getDbList).toBeCalledTimes(1);
|
||||
expect(SupersetClientGet).toBeCalledTimes(2);
|
||||
expect(props.getDbList).toBeCalledTimes(0);
|
||||
expect(props.getTableList).toBeCalledTimes(0);
|
||||
expect(props.handleError).toBeCalledTimes(0);
|
||||
expect(props.onDbChange).toBeCalledTimes(1);
|
||||
expect(props.onSchemaChange).toBeCalledTimes(1);
|
||||
expect(props.onDbChange).toBeCalledTimes(0);
|
||||
expect(props.onSchemaChange).toBeCalledTimes(0);
|
||||
expect(props.onSchemasLoad).toBeCalledTimes(2);
|
||||
expect(props.onUpdate).toBeCalledTimes(1);
|
||||
expect(props.onUpdate).toBeCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('Should database select display options', async () => {
|
||||
const props = createProps();
|
||||
render(<DatabaseSelector {...props} />);
|
||||
const selector = await screen.findByText('Database:');
|
||||
expect(selector).toBeInTheDocument();
|
||||
expect(selector.parentElement).toHaveTextContent(
|
||||
'Database:postgresql examples',
|
||||
);
|
||||
const select = screen.getByRole('combobox', {
|
||||
name: 'Select database or type database name',
|
||||
});
|
||||
expect(select).toBeInTheDocument();
|
||||
userEvent.click(select);
|
||||
expect(await screen.findByText('test-mysql')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Should schema select display options', async () => {
|
||||
const props = createProps();
|
||||
render(<DatabaseSelector {...props} />);
|
||||
|
||||
const selector = await screen.findByText('Schema:');
|
||||
expect(selector).toBeInTheDocument();
|
||||
expect(selector.parentElement).toHaveTextContent('Schema: public');
|
||||
|
||||
userEvent.click(screen.getByRole('button'));
|
||||
|
||||
expect(await screen.findByText('Select a schema (2)')).toBeInTheDocument();
|
||||
const select = screen.getByRole('combobox', {
|
||||
name: 'Select schema or type schema name',
|
||||
});
|
||||
expect(select).toBeInTheDocument();
|
||||
userEvent.click(select);
|
||||
expect(
|
||||
await screen.findByRole('option', { name: 'public' }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByRole('option', { name: 'information_schema' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Sends the correct db when changing the database', async () => {
|
||||
const props = createProps();
|
||||
render(<DatabaseSelector {...props} />);
|
||||
const select = screen.getByRole('combobox', {
|
||||
name: 'Select database or type database name',
|
||||
});
|
||||
expect(select).toBeInTheDocument();
|
||||
userEvent.click(select);
|
||||
userEvent.click(await screen.findByText('test-mysql'));
|
||||
await waitFor(() =>
|
||||
expect(props.onDbChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
database_name: 'test-mysql',
|
||||
backend: 'mysql',
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('Sends the correct schema when changing the schema', async () => {
|
||||
const props = createProps();
|
||||
render(<DatabaseSelector {...props} />);
|
||||
const select = screen.getByRole('combobox', {
|
||||
name: 'Select schema or type schema name',
|
||||
});
|
||||
expect(select).toBeInTheDocument();
|
||||
userEvent.click(select);
|
||||
const schemaOption = await screen.findAllByText('information_schema');
|
||||
userEvent.click(schemaOption[1]);
|
||||
await waitFor(() =>
|
||||
expect(props.onSchemaChange).toHaveBeenCalledWith('information_schema'),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -16,80 +16,94 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { ReactNode, useEffect, useState } from 'react';
|
||||
import React, { ReactNode, useState, useMemo, useEffect } from 'react';
|
||||
import { styled, SupersetClient, t } from '@superset-ui/core';
|
||||
import rison from 'rison';
|
||||
import { Select } from 'src/components/Select';
|
||||
import { Select } from 'src/components';
|
||||
import Label from 'src/components/Label';
|
||||
import { FormLabel } from 'src/components/Form';
|
||||
import RefreshLabel from 'src/components/RefreshLabel';
|
||||
import SupersetAsyncSelect from 'src/components/AsyncSelect';
|
||||
|
||||
const FieldTitle = styled.p`
|
||||
color: ${({ theme }) => theme.colors.secondary.light2};
|
||||
font-size: ${({ theme }) => theme.typography.sizes.s}px;
|
||||
margin: 20px 0 10px 0;
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const DatabaseSelectorWrapper = styled.div`
|
||||
.fa-refresh {
|
||||
padding-left: 9px;
|
||||
}
|
||||
${({ theme }) => `
|
||||
.refresh {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
margin-left: ${theme.gridUnit}px;
|
||||
margin-top: ${theme.gridUnit * 5}px;
|
||||
}
|
||||
|
||||
.refresh-col {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
margin-left: ${({ theme }) => theme.gridUnit}px;
|
||||
}
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding-bottom: 5px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.select {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.select {
|
||||
flex-grow: 1;
|
||||
}
|
||||
& > div {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const DatabaseOption = styled.span`
|
||||
display: inline-flex;
|
||||
const LabelStyle = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-left: ${({ theme }) => theme.gridUnit - 2}px;
|
||||
`;
|
||||
|
||||
type DatabaseValue = {
|
||||
label: React.ReactNode;
|
||||
value: number;
|
||||
id: number;
|
||||
database_name: string;
|
||||
backend: string;
|
||||
};
|
||||
|
||||
type SchemaValue = { label: string; value: string };
|
||||
|
||||
interface DatabaseSelectorProps {
|
||||
dbId: number;
|
||||
db?: { id: number; database_name: string; backend: string };
|
||||
formMode?: boolean;
|
||||
getDbList?: (arg0: any) => {};
|
||||
getTableList?: (dbId: number, schema: string, force: boolean) => {};
|
||||
handleError: (msg: string) => void;
|
||||
isDatabaseSelectEnabled?: boolean;
|
||||
onDbChange?: (db: any) => void;
|
||||
onSchemaChange?: (arg0?: any) => {};
|
||||
onDbChange?: (db: {
|
||||
id: number;
|
||||
database_name: string;
|
||||
backend: string;
|
||||
}) => void;
|
||||
onSchemaChange?: (schema?: string) => void;
|
||||
onSchemasLoad?: (schemas: Array<object>) => void;
|
||||
readOnly?: boolean;
|
||||
schema?: string;
|
||||
sqlLabMode?: boolean;
|
||||
onUpdate?: ({
|
||||
dbId,
|
||||
schema,
|
||||
}: {
|
||||
dbId: number;
|
||||
schema?: string;
|
||||
tableName?: string;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
const SelectLabel = ({
|
||||
backend,
|
||||
databaseName,
|
||||
}: {
|
||||
backend: string;
|
||||
databaseName: string;
|
||||
}) => (
|
||||
<LabelStyle>
|
||||
<Label>{backend}</Label>
|
||||
{databaseName}
|
||||
</LabelStyle>
|
||||
);
|
||||
|
||||
export default function DatabaseSelector({
|
||||
dbId,
|
||||
db,
|
||||
formMode = false,
|
||||
getDbList,
|
||||
getTableList,
|
||||
handleError,
|
||||
isDatabaseSelectEnabled = true,
|
||||
onUpdate,
|
||||
onDbChange,
|
||||
onSchemaChange,
|
||||
onSchemasLoad,
|
||||
@@ -97,193 +111,188 @@ export default function DatabaseSelector({
|
||||
schema,
|
||||
sqlLabMode = false,
|
||||
}: DatabaseSelectorProps) {
|
||||
const [currentDbId, setCurrentDbId] = useState(dbId);
|
||||
const [currentSchema, setCurrentSchema] = useState<string | undefined>(
|
||||
schema,
|
||||
const [loadingSchemas, setLoadingSchemas] = useState(false);
|
||||
const [schemaOptions, setSchemaOptions] = useState<SchemaValue[]>([]);
|
||||
const [currentDb, setCurrentDb] = useState<DatabaseValue | undefined>(
|
||||
db
|
||||
? {
|
||||
label: (
|
||||
<SelectLabel backend={db.backend} databaseName={db.database_name} />
|
||||
),
|
||||
value: db.id,
|
||||
...db,
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
const [schemaLoading, setSchemaLoading] = useState(false);
|
||||
const [schemaOptions, setSchemaOptions] = useState([]);
|
||||
const [currentSchema, setCurrentSchema] = useState<SchemaValue | undefined>(
|
||||
schema ? { label: schema, value: schema } : undefined,
|
||||
);
|
||||
const [refresh, setRefresh] = useState(0);
|
||||
|
||||
function fetchSchemas(databaseId: number, forceRefresh = false) {
|
||||
const actualDbId = databaseId || dbId;
|
||||
if (actualDbId) {
|
||||
setSchemaLoading(true);
|
||||
const loadDatabases = useMemo(
|
||||
() => async (
|
||||
search: string,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
): Promise<{
|
||||
data: DatabaseValue[];
|
||||
totalCount: number;
|
||||
}> => {
|
||||
const queryParams = rison.encode({
|
||||
force: Boolean(forceRefresh),
|
||||
order_columns: 'database_name',
|
||||
order_direction: 'asc',
|
||||
page,
|
||||
page_size: pageSize,
|
||||
...(formMode || !sqlLabMode
|
||||
? { filters: [{ col: 'database_name', opr: 'ct', value: search }] }
|
||||
: {
|
||||
filters: [
|
||||
{ col: 'database_name', opr: 'ct', value: search },
|
||||
{
|
||||
col: 'expose_in_sqllab',
|
||||
opr: 'eq',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
const endpoint = `/api/v1/database/${actualDbId}/schemas/?q=${queryParams}`;
|
||||
return SupersetClient.get({ endpoint })
|
||||
.then(({ json }) => {
|
||||
const options = json.result.map((s: string) => ({
|
||||
value: s,
|
||||
label: s,
|
||||
title: s,
|
||||
}));
|
||||
setSchemaOptions(options);
|
||||
setSchemaLoading(false);
|
||||
const endpoint = `/api/v1/database/?q=${queryParams}`;
|
||||
return SupersetClient.get({ endpoint }).then(({ json }) => {
|
||||
const { result } = json;
|
||||
if (getDbList) {
|
||||
getDbList(result);
|
||||
}
|
||||
if (result.length === 0) {
|
||||
handleError(t("It seems you don't have access to any database"));
|
||||
}
|
||||
const options = result.map(
|
||||
(row: { id: number; database_name: string; backend: string }) => ({
|
||||
label: (
|
||||
<SelectLabel
|
||||
backend={row.backend}
|
||||
databaseName={row.database_name}
|
||||
/>
|
||||
),
|
||||
value: row.id,
|
||||
id: row.id,
|
||||
database_name: row.database_name,
|
||||
backend: row.backend,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
data: options,
|
||||
totalCount: options.length,
|
||||
};
|
||||
});
|
||||
},
|
||||
[formMode, getDbList, handleError, sqlLabMode],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentDb) {
|
||||
setLoadingSchemas(true);
|
||||
const queryParams = rison.encode({ force: refresh > 0 });
|
||||
const endpoint = `/api/v1/database/${currentDb.value}/schemas/?q=${queryParams}`;
|
||||
|
||||
try {
|
||||
// TODO: Would be nice to add pagination in a follow-up. Needs endpoint changes.
|
||||
SupersetClient.get({ endpoint }).then(({ json }) => {
|
||||
const options = json.result
|
||||
.map((s: string) => ({
|
||||
value: s,
|
||||
label: s,
|
||||
title: s,
|
||||
}))
|
||||
.sort((a: { label: string }, b: { label: string }) =>
|
||||
a.label.localeCompare(b.label),
|
||||
);
|
||||
if (onSchemasLoad) {
|
||||
onSchemasLoad(options);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setSchemaOptions([]);
|
||||
setSchemaLoading(false);
|
||||
handleError(t('Error while fetching schema list'));
|
||||
setSchemaOptions(options);
|
||||
});
|
||||
} finally {
|
||||
setLoadingSchemas(false);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}, [currentDb, onSchemasLoad, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentDbId) {
|
||||
fetchSchemas(currentDbId);
|
||||
}
|
||||
}, [currentDbId]);
|
||||
|
||||
function onSelectChange({ dbId, schema }: { dbId: number; schema?: string }) {
|
||||
setCurrentDbId(dbId);
|
||||
setCurrentSchema(schema);
|
||||
if (onUpdate) {
|
||||
onUpdate({ dbId, schema, tableName: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
function dbMutator(data: any) {
|
||||
if (getDbList) {
|
||||
getDbList(data.result);
|
||||
}
|
||||
if (data.result.length === 0) {
|
||||
handleError(t("It seems you don't have access to any database"));
|
||||
}
|
||||
return data.result.map((row: any) => ({
|
||||
...row,
|
||||
// label is used for the typeahead
|
||||
label: `${row.backend} ${row.database_name}`,
|
||||
}));
|
||||
}
|
||||
|
||||
function changeDataBase(db: any, force = false) {
|
||||
const dbId = db ? db.id : null;
|
||||
setSchemaOptions([]);
|
||||
if (onSchemaChange) {
|
||||
onSchemaChange(null);
|
||||
}
|
||||
function changeDataBase(
|
||||
value: { label: string; value: number },
|
||||
database: DatabaseValue,
|
||||
) {
|
||||
setCurrentDb(database);
|
||||
setCurrentSchema(undefined);
|
||||
if (onDbChange) {
|
||||
onDbChange(db);
|
||||
onDbChange(database);
|
||||
}
|
||||
fetchSchemas(dbId, force);
|
||||
onSelectChange({ dbId, schema: undefined });
|
||||
}
|
||||
|
||||
function changeSchema(schemaOpt: any, force = false) {
|
||||
const schema = schemaOpt ? schemaOpt.value : null;
|
||||
if (onSchemaChange) {
|
||||
onSchemaChange(schema);
|
||||
}
|
||||
setCurrentSchema(schema);
|
||||
onSelectChange({ dbId: currentDbId, schema });
|
||||
if (getTableList) {
|
||||
getTableList(currentDbId, schema, force);
|
||||
onSchemaChange(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function renderDatabaseOption(db: any) {
|
||||
return (
|
||||
<DatabaseOption title={db.database_name}>
|
||||
<Label type="default">{db.backend}</Label> {db.database_name}
|
||||
</DatabaseOption>
|
||||
);
|
||||
function changeSchema(schema: SchemaValue) {
|
||||
setCurrentSchema(schema);
|
||||
if (onSchemaChange) {
|
||||
onSchemaChange(schema.value);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSelectRow(select: ReactNode, refreshBtn: ReactNode) {
|
||||
return (
|
||||
<div className="section">
|
||||
<span className="select">{select}</span>
|
||||
<span className="refresh-col">{refreshBtn}</span>
|
||||
<span className="refresh">{refreshBtn}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderDatabaseSelect() {
|
||||
const queryParams = rison.encode({
|
||||
order_columns: 'database_name',
|
||||
order_direction: 'asc',
|
||||
page: 0,
|
||||
page_size: -1,
|
||||
...(formMode || !sqlLabMode
|
||||
? {}
|
||||
: {
|
||||
filters: [
|
||||
{
|
||||
col: 'expose_in_sqllab',
|
||||
opr: 'eq',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
return renderSelectRow(
|
||||
<SupersetAsyncSelect
|
||||
<Select
|
||||
ariaLabel={t('Select database or type database name')}
|
||||
data-test="select-database"
|
||||
dataEndpoint={`/api/v1/database/?q=${queryParams}`}
|
||||
onChange={(db: any) => changeDataBase(db)}
|
||||
onAsyncError={() =>
|
||||
handleError(t('Error while fetching database list'))
|
||||
}
|
||||
clearable={false}
|
||||
value={currentDbId}
|
||||
valueKey="id"
|
||||
valueRenderer={(db: any) => (
|
||||
<div>
|
||||
<span className="text-muted m-r-5">{t('Database:')}</span>
|
||||
{renderDatabaseOption(db)}
|
||||
</div>
|
||||
)}
|
||||
optionRenderer={renderDatabaseOption}
|
||||
mutator={dbMutator}
|
||||
placeholder={t('Select a database')}
|
||||
autoSelect
|
||||
isDisabled={!isDatabaseSelectEnabled || readOnly}
|
||||
header={<FormLabel>{t('Database')}</FormLabel>}
|
||||
onChange={changeDataBase}
|
||||
value={currentDb}
|
||||
placeholder={t('Select database or type database name')}
|
||||
disabled={!isDatabaseSelectEnabled || readOnly}
|
||||
options={loadDatabases}
|
||||
/>,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
function renderSchemaSelect() {
|
||||
const value = schemaOptions.filter(({ value }) => currentSchema === value);
|
||||
const refresh = !formMode && !readOnly && (
|
||||
const refreshIcon = !formMode && !readOnly && (
|
||||
<RefreshLabel
|
||||
onClick={() => changeDataBase({ id: dbId }, true)}
|
||||
onClick={() => setRefresh(refresh + 1)}
|
||||
tooltipContent={t('Force refresh schema list')}
|
||||
/>
|
||||
);
|
||||
|
||||
return renderSelectRow(
|
||||
<Select
|
||||
ariaLabel={t('Select schema or type schema name')}
|
||||
disabled={readOnly}
|
||||
header={<FormLabel>{t('Schema')}</FormLabel>}
|
||||
labelInValue
|
||||
lazyLoading={false}
|
||||
loading={loadingSchemas}
|
||||
name="select-schema"
|
||||
placeholder={t('Select a schema (%s)', schemaOptions.length)}
|
||||
placeholder={t('Select schema or type schema name')}
|
||||
onChange={item => changeSchema(item as SchemaValue)}
|
||||
options={schemaOptions}
|
||||
value={value}
|
||||
valueRenderer={o => (
|
||||
<div>
|
||||
<span className="text-muted">{t('Schema:')}</span> {o.label}
|
||||
</div>
|
||||
)}
|
||||
isLoading={schemaLoading}
|
||||
autosize={false}
|
||||
onChange={item => changeSchema(item)}
|
||||
isDisabled={readOnly}
|
||||
showSearch
|
||||
value={currentSchema}
|
||||
/>,
|
||||
refresh,
|
||||
refreshIcon,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DatabaseSelectorWrapper data-test="DatabaseSelector">
|
||||
{formMode && <FieldTitle>{t('datasource')}</FieldTitle>}
|
||||
{renderDatabaseSelect()}
|
||||
{formMode && <FieldTitle>{t('schema')}</FieldTitle>}
|
||||
{renderSchemaSelect()}
|
||||
</DatabaseSelectorWrapper>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user