Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tableselector to dataset creation page #21075

Merged
merged 34 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
85bcbca
add database selector
pkdotson Aug 5, 2022
cd87fda
add schema util
pkdotson Aug 8, 2022
413aea9
add pagination
pkdotson Aug 12, 2022
aeb0d6f
remove conosle.logs
pkdotson Aug 12, 2022
0f0ded9
add search
pkdotson Aug 18, 2022
7e1ca34
remove logs
pkdotson Aug 18, 2022
96b609a
Merge branch 'master' of https://github.com/preset-io/superset into f…
pkdotson Aug 23, 2022
3bbe070
more updates
pkdotson Aug 24, 2022
23e03b1
update loading style and add state
pkdotson Aug 24, 2022
b0cf236
remove header styles
pkdotson Aug 25, 2022
3c680b5
add test, fix types, other updates
pkdotson Aug 25, 2022
8e5d195
Update superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftP…
pkdotson Aug 25, 2022
1ffcf86
Update superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftP…
pkdotson Aug 25, 2022
b7fb96d
Update superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftP…
pkdotson Aug 25, 2022
2fbc0b1
Update superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftP…
pkdotson Aug 25, 2022
d982996
Update superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftP…
pkdotson Aug 25, 2022
16ae23b
Update superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftP…
pkdotson Aug 25, 2022
e0097b2
fix types
pkdotson Aug 26, 2022
434f934
Merge branch 'feat-add-tableselector' of https://github.com/preset-io…
pkdotson Aug 26, 2022
472a27b
fix styles
pkdotson Aug 26, 2022
d3ed28e
Merge branch 'master' of https://github.com/preset-io/superset into f…
pkdotson Aug 29, 2022
48b15b2
update type
pkdotson Aug 29, 2022
ee25dc7
fix tests
pkdotson Aug 30, 2022
a4258ba
fix test
pkdotson Aug 30, 2022
4ce4d29
run precommit
pkdotson Aug 30, 2022
a02db58
add suggestions
pkdotson Aug 31, 2022
1689774
match styles and desgin
pkdotson Sep 1, 2022
b169378
update test
pkdotson Sep 1, 2022
2012690
update test and last changes
pkdotson Sep 6, 2022
9745842
Merge branch 'master' of https://github.com/preset-io/superset into f…
pkdotson Sep 6, 2022
e81484f
fix merge
pkdotson Sep 6, 2022
f0dd386
fix indentation
pkdotson Sep 6, 2022
4057c66
remove log
pkdotson Sep 6, 2022
8a92b3e
fix parent component tests
pkdotson Sep 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions superset-frontend/src/components/TableSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ interface TableSelectorProps {
tableSelectMode?: 'single' | 'multiple';
}

interface Table {
export interface Table {
label: string;
value: string;
type: string;
Expand All @@ -114,13 +114,13 @@ interface Table {
};
}

interface TableOption {
export interface TableOption {
label: JSX.Element;
text: string;
value: string;
}

const TableOption = ({ table }: { table: Table }) => {
export const TableOption = ({ table }: { table: Table }) => {
const { label, type, extra } = table;
return (
<TableLabel title={label}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import AddDataset from 'src/views/CRUD/data/dataset/AddDataset';

describe('AddDataset', () => {
it('renders a blank state AddDataset', () => {
render(<AddDataset />);
render(<AddDataset />, { useRedux: true });

const blankeStateImgs = screen.getAllByRole('img', { name: /empty/i });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,69 @@
* under the License.
*/
import React from 'react';
import { SupersetClient } from '@superset-ui/core';
import { render, screen } from 'spec/helpers/testing-library';
import LeftPanel from 'src/views/CRUD/data/dataset/AddDataset/LeftPanel';

describe('LeftPanel', () => {
it('renders a blank state LeftPanel', () => {
render(<LeftPanel />);
const mockFun = jest.fn();

const SupersetClientGet = jest.spyOn(SupersetClient, 'get');

const getSchemaMockFunction = async () =>
({
json: {
result: ['schema_a', 'schema_b'],
},
} as any);

const getTableMockFunction = async () =>
({
json: {
options: [
{ label: 'table_a', value: 'table_a' },
{ label: 'table_b', value: 'table_b' },
{ label: 'table_c', value: 'table_c' },
{ label: 'table_d', value: 'table_d' },
],
},
} as any);

it('should render', () => {
const { container } = render(<LeftPanel setDataset={mockFun} />, {
useRedux: true,
});
expect(container).toBeInTheDocument();
});

it('should render tableselector and databaselector container and selects', () => {
render(<LeftPanel setDataset={mockFun} />, { useRedux: true });

expect(screen.getByText(/select database & schema/i)).toBeVisible();

const databaseSelect = screen.getByRole('combobox', {
name: 'Select database or type database name',
});
const schemaSelect = screen.getByRole('combobox', {
name: 'Select schema or type schema name',
});
expect(databaseSelect).toBeInTheDocument();
expect(schemaSelect).toBeInTheDocument();
});
it('renders a blank state LeftPanel if there is no schema selected', () => {
render(<LeftPanel setDataset={mockFun} />, { useRedux: true });

expect(screen.getByRole('img', { name: /empty/i })).toBeVisible();
expect(screen.getByText(/no database tables found/i)).toBeVisible();
expect(screen.getByText(/try selecting a different schema/i)).toBeVisible();
});
it('renders list of options when user clicks on schema', () => {
render(<LeftPanel setDataset={mockFun} schema="schema_a" dbId={1} />, {
useRedux: true,
});
// screen.debug('container', container)
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
SupersetClientGet.mockImplementation(getSchemaMockFunction);
SupersetClientGet.mockImplementation(getTableMockFunction);
expect(screen.getByTestId('options-list')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,227 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { t } from '@superset-ui/core';
import React, {
useEffect,
useState,
useMemo,
SetStateAction,
Dispatch,
} from 'react';
import { SupersetClient, t, styled, FAST_DEBOUNCE } from '@superset-ui/core';
import { Input } from 'src/components/Input';
import { Form } from 'src/components/Form';
import { TableOption, Table } from 'src/components/TableSelector';
import RefreshLabel from 'src/components/RefreshLabel';
import Loading from 'src/components/Loading';
import DatabaseSelector from 'src/components/DatabaseSelector';
import { debounce } from 'lodash';
import { EmptyStateMedium } from 'src/components/EmptyState';
import { useToasts } from 'src/components/MessageToasts/withToasts';
import { DatasetActionType, DatasetObject } from '../types';

interface LeftPanelProps {
setDataset: Dispatch<SetStateAction<object>>;
schema?: string | undefined | null;
dbId?: number;
}

const LeftPanelStyle = styled.div`
${({ theme }) => `
max-width: ${theme.gridUnit * 87.5}px;
padding: ${theme.gridUnit * 4}px;
height: 100%;
background-color: ${theme.colors.grayscale.light5};
position: relative;
.emptystate {
height: auto;
margin-top: 70px;
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
}
.refresh {
position: absolute;
top: ${theme.gridUnit * 43.25}px;
left: ${theme.gridUnit * 16.75}px;
span[role="button"]{
font-size: ${theme.gridUnit * 4.25}px;
}
}
.section-title {
margin-top: ${theme.gridUnit * 11}px;
margin-bottom: ${theme.gridUnit * 11}px;
font-weight: ${theme.typography.weights.bold};
}
.table-title {
margin-top: ${theme.gridUnit * 11}px;
margin-bottom: ${theme.gridUnit * 6}px;
font-weight: ${theme.typography.weights.bold};
}
.options-list {
overflow: auto;
position: absolute;
bottom: 0px;
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
top: 390px;
left: 13px;
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
right: 0;
.options {
padding: ${theme.gridUnit * 1.75}px;
border-radius: ${theme.borderRadius}px;
}
}
form > span {
position: absolute;
top: ${theme.gridUnit * 73}px;
left: ${theme.gridUnit * 42.75}px;
font-size: ${theme.gridUnit * 4.25}px;
}
.table-form {
margin-bottom: ${theme.gridUnit * 8}px;
}
.loading {
position: absolute;
bottom: ${theme.gridUnit * 95}px;
img {
position: absolute;
top: -${theme.gridUnit * 13.25}px;
right: -${theme.gridUnit * 3.75}px;
width: ${theme.gridUnit * 17.75}px;
}
}
}
`}
`;

export default function LeftPanel({
setDataset,
schema,
dbId,
}: LeftPanelProps) {
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 { addDangerToast } = useToasts();

const setDatabase = (db: Partial<DatasetObject>) => {
setDataset({ type: DatasetActionType.selectDatabase, payload: db });
setResetTables(true);
};

const getTablesList = (url: string) => {
SupersetClient.get({ url })
.then(({ json }) => {
const options: TableOption[] = json.options.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(e => {
console.log('error', e);
});
};

const setSchema = (schema: string) => {
if (schema) {
setDataset({ type: DatasetActionType.selectSchema, payload: schema });
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
setLoadTables(true);
}
setResetTables(true);
};

const encodedSchema = encodeURIComponent(schema as string);
pkdotson marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (loadTables) {
const endpoint = encodeURI(
`/superset/tables/${dbId}/${encodedSchema}/undefined/${refresh}/`,
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
);
getTablesList(endpoint);
}
}, [loadTables]);

useEffect(() => {
if (resetTables) {
setTableOptions([]);
setResetTables(false);
}
}, [resetTables]);

const search = useMemo(
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
() =>
debounce((value: string) => {
const encodeTableName =
value === '' ? undefined : encodeURIComponent(value);
const endpoint = encodeURI(
`/superset/tables/${dbId}/${encodedSchema}/${encodeTableName}/`,
);
getTablesList(endpoint);
}, FAST_DEBOUNCE),
[dbId, encodedSchema],
);

export default function LeftPanel() {
return (
<>
<EmptyStateMedium
image="empty-table.svg"
title={t('No database tables found')}
description={t('Try selecting a different schema')}
<LeftPanelStyle>
<p className="section-title db-schema"> Select Database & Schema</p>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<p className="section-title db-schema"> Select Database & Schema</p>
<p className="section-title db-schema">Select Database & Schema</p>

Is this space meant to be here?

<DatabaseSelector
handleError={addDangerToast}
onDbChange={setDatabase}
onSchemaChange={setSchema}
/>
</>
{loadTables && (
<div className="loading">
<Loading position="inline" />
<p>loading schemas ...</p>
pkdotson marked this conversation as resolved.
Show resolved Hide resolved
</div>
)}
{!schema && !loadTables ? (
<div className="emptystate">
<EmptyStateMedium
image="empty-table.svg"
title={t('No database tables found')}
description={t('Try selecting a different schema')}
/>
</div>
) : (
<>
<Form>
<p className="table-title">Select Database Table</p>
<RefreshLabel
onClick={() => {
setLoadTables(true);
setRefresh(true);
}}
tooltipContent={t('Refresh table list')}
/>
<Input
value={searchVal}
onChange={evt => {
search(evt.target.value);
setSearchVal(evt.target.value);
}}
className="table-form"
placeholder={t('Search Tables')}
/>
</Form>
<div className="options-list" data-test="options-list">
{tableOptions.map((o, i) => (
<div className="options" key={i}>
{o.label}
</div>
))}
</div>
</>
)}
</LeftPanelStyle>
);
}
Loading