fix(datasets): consistent dataset list (#15014)

This commit is contained in:
Yongjie Zhao 2021-06-10 03:05:33 +01:00 committed by GitHub
parent e8414dfb52
commit 834bb9409d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 63 deletions

View File

@ -48,7 +48,7 @@ const datasourceData = {
};
const DATASOURCES_ENDPOINT =
'glob:*/api/v1/dataset/?q=(order_column:changed_on_delta_humanized,order_direction:asc,page:0,page_size:20)';
'glob:*/api/v1/dataset/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)';
const DATASOURCE_ENDPOINT = `glob:*/datasource/get/${datasourceData.type}/${datasourceData.id}`;
const DATASOURCE_PAYLOAD = { new: 'data' };

View File

@ -16,12 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import React, { useEffect } from 'react';
import isEqual from 'lodash/isEqual';
import { styled, t } from '@superset-ui/core';
import { useFilters, usePagination, useSortBy, useTable } from 'react-table';
import { Empty } from 'src/common/components';
import { TableCollection, Pagination } from 'src/components/dataViewCommon';
import { SortColumns } from './types';
import { SortByType, ServerPagination } from './types';
const DEFAULT_PAGE_SIZE = 10;
@ -34,8 +35,11 @@ export interface TableViewProps {
columns: any[];
data: any[];
pageSize?: number;
totalCount?: number;
serverPagination?: boolean;
onServerPagination?: (args: ServerPagination) => void;
initialPageIndex?: number;
initialSortBy?: SortColumns;
initialSortBy?: SortByType;
loading?: boolean;
withPagination?: boolean;
emptyWrapperType?: EmptyWrapperType;
@ -57,13 +61,17 @@ const TableViewStyles = styled.div<{
${({ scrollTable, theme }) =>
scrollTable &&
`
height: 300px;
height: 380px;
margin-bottom: ${theme.gridUnit * 4}px;
overflow: auto;
`}
.table-cell.table-cell {
vertical-align: top;
.table-row {
height: 43px;
}
th[role='columnheader'] {
z-index: 1;
}
.pagination-container {
@ -92,6 +100,7 @@ const TableView = ({
columns,
data,
pageSize: initialPageSize,
totalCount = data.length,
initialPageIndex,
initialSortBy = [],
loading = false,
@ -99,6 +108,8 @@ const TableView = ({
emptyWrapperType = EmptyWrapperType.Default,
noDataText,
showRowCount = true,
serverPagination = false,
onServerPagination = () => {},
...props
}: TableViewProps) => {
const initialState = {
@ -116,18 +127,38 @@ const TableView = ({
prepareRow,
pageCount,
gotoPage,
state: { pageIndex, pageSize },
state: { pageIndex, pageSize, sortBy },
} = useTable(
{
columns,
data,
initialState,
manualPagination: serverPagination,
manualSortBy: serverPagination,
pageCount: Math.ceil(totalCount / initialState.pageSize),
},
useFilters,
useSortBy,
usePagination,
);
useEffect(() => {
if (serverPagination && pageIndex !== initialState.pageIndex) {
onServerPagination({
pageIndex,
});
}
}, [pageIndex]);
useEffect(() => {
if (serverPagination && !isEqual(sortBy, initialState.sortBy)) {
onServerPagination({
pageIndex: 0,
sortBy,
});
}
}, [sortBy]);
const content = withPagination ? page : rows;
let EmptyWrapperComponent;
@ -182,7 +213,7 @@ const TableView = ({
'%s-%s of %s',
pageSize * pageIndex + (page.length && 1),
pageSize * pageIndex + page.length,
data.length,
totalCount,
)}
</div>
)}

View File

@ -16,9 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
export interface SortColumn {
id: string;
desc?: boolean;
}
import { SortingRule } from 'react-table';
export type SortColumns = SortColumn[];
export type SortByType = SortingRule<string>[];
export interface ServerPagination {
pageIndex: number;
sortBy?: SortByType;
}

View File

@ -26,15 +26,22 @@ import React, {
import Alert from 'src/components/Alert';
import { SupersetClient, t, styled } from '@superset-ui/core';
import TableView, { EmptyWrapperType } from 'src/components/TableView';
import { ServerPagination, SortByType } from 'src/components/TableView/types';
import StyledModal from 'src/components/Modal';
import Button from 'src/components/Button';
import { useListViewResource } from 'src/views/CRUD/hooks';
import Dataset from 'src/types/Dataset';
import { useDebouncedEffect } from 'src/explore/exploreUtils';
import { SLOW_DEBOUNCE } from 'src/constants';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import Loading from 'src/components/Loading';
import withToasts from 'src/messageToasts/enhancers/withToasts';
import { Input, AntdInput } from 'src/common/components';
import {
PAGE_SIZE as DATASET_PAGE_SIZE,
SORT_BY as DATASET_SORT_BY,
} from 'src/views/CRUD/data/dataset/constants';
import FacePile from '../components/FacePile';
const CONFIRM_WARNING_MESSAGE = t(
'Warning! Changing the dataset may break the chart if the metadata does not exist.',
@ -81,21 +88,6 @@ const StyledSpan = styled.span`
}
`;
const TABLE_COLUMNS = [
'name',
'type',
'schema',
'connection',
'creator',
].map(col => ({ accessor: col, Header: col }));
const emptyRequest = {
pageIndex: 0,
pageSize: 20,
filters: [],
sortBy: [{ id: 'changed_on_delta_humanized' }],
};
const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
addDangerToast,
addSuccessToast,
@ -105,12 +97,14 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
show,
}) => {
const [filter, setFilter] = useState<any>(undefined);
const [pageIndex, setPageIndex] = useState<number>(0);
const [sortBy, setSortBy] = useState<SortByType>(DATASET_SORT_BY);
const [confirmChange, setConfirmChange] = useState(false);
const [confirmedDataset, setConfirmedDataset] = useState<Datasource>();
const searchRef = useRef<AntdInput>(null);
const {
state: { loading, resourceCollection },
state: { loading, resourceCollection, resourceCount },
fetchData,
} = useListViewResource<Dataset>('dataset', t('dataset'), addDangerToast);
@ -119,10 +113,17 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
setConfirmedDataset(datasource);
}, []);
const fetchDatasetPayload = {
pageIndex,
pageSize: DATASET_PAGE_SIZE,
filters: [],
sortBy,
};
useDebouncedEffect(
() => {
fetchData({
...emptyRequest,
...fetchDatasetPayload,
...(filter && {
filters: [
{
@ -134,8 +135,8 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
}),
});
},
300,
[filter],
SLOW_DEBOUNCE,
[filter, pageIndex, sortBy],
);
useEffect(() => {
@ -159,6 +160,7 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
const changeSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
const searchValue = event.target.value ?? '';
setFilter(searchValue);
setPageIndex(0);
};
const handleChangeConfirm = () => {
@ -187,25 +189,53 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
setConfirmChange(false);
};
const renderTableView = () => {
const data = resourceCollection.map((ds: any) => ({
rawName: ds.table_name,
connection: ds.database.database_name,
schema: ds.schema,
name: (
const columns = [
{
Cell: ({ row: { original } }: any) => (
<StyledSpan
role="button"
tabIndex={0}
data-test="datasource-link"
onClick={() => selectDatasource({ type: 'table', ...ds })}
onClick={() => selectDatasource({ type: 'table', ...original })}
>
{ds.table_name}
{original?.table_name}
</StyledSpan>
),
type: ds.kind,
}));
Header: t('Name'),
accessor: 'table_name',
},
{
Header: t('Type'),
accessor: 'kind',
disableSortBy: true,
},
{
Header: t('Schema'),
accessor: 'schema',
},
{
Header: t('Connection'),
accessor: 'database.database_name',
disableSortBy: true,
},
{
Cell: ({
row: {
original: { owners = [] },
},
}: any) => <FacePile users={owners} />,
Header: t('Owners'),
id: 'owners',
disableSortBy: true,
},
];
return data;
const onServerPagination = (args: ServerPagination) => {
setPageIndex(args.pageIndex);
if (args.sortBy) {
// ensure default sort by
setSortBy(args.sortBy.length > 0 ? args.sortBy : DATASET_SORT_BY);
}
};
return (
@ -215,7 +245,7 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
responsive
title={t('Change dataset')}
width={confirmChange ? '432px' : ''}
height={confirmChange ? 'auto' : '480px'}
height={confirmChange ? 'auto' : '540px'}
hideFooter={!confirmChange}
footer={
<>
@ -259,11 +289,17 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
{loading && <Loading />}
{!loading && (
<TableView
columns={TABLE_COLUMNS}
data={renderTableView()}
pageSize={20}
columns={columns}
data={resourceCollection}
pageSize={DATASET_PAGE_SIZE}
initialPageIndex={pageIndex}
initialSortBy={sortBy}
totalCount={resourceCount}
onServerPagination={onServerPagination}
className="table-condensed"
emptyWrapperType={EmptyWrapperType.Small}
serverPagination
isPaginationSticky
scrollTable
/>
)}

View File

@ -53,20 +53,12 @@ import ImportModelsModal from 'src/components/ImportModal/index';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip';
import AddDatasetModal from './AddDatasetModal';
const PAGE_SIZE = 25;
const PASSWORDS_NEEDED_MESSAGE = t(
'The passwords for the databases below are needed in order to ' +
'import them together with the datasets. Please note that the ' +
'"Secure Extra" and "Certificate" sections of ' +
'the database configuration are not present in export files, and ' +
'should be added manually after the import if they are needed.',
);
const CONFIRM_OVERWRITE_MESSAGE = t(
'You are importing one or more datasets that already exist. ' +
'Overwriting might cause you to lose some of your work. Are you ' +
'sure you want to overwrite?',
);
import {
PAGE_SIZE,
SORT_BY,
PASSWORDS_NEEDED_MESSAGE,
CONFIRM_OVERWRITE_MESSAGE,
} from './constants';
const FlexRowContainer = styled.div`
align-items: center;
@ -158,7 +150,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
const canCreate = hasPerm('can_write');
const canExport = hasPerm('can_read');
const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
const initialSort = SORT_BY;
const openDatasetEditModal = useCallback(
({ id }: Dataset) => {

View File

@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { t } from '@superset-ui/core';
export const PAGE_SIZE = 25;
export const SORT_BY = [{ id: 'changed_on_delta_humanized', desc: true }];
export const PASSWORDS_NEEDED_MESSAGE = t(
'The passwords for the databases below are needed in order to ' +
'import them together with the datasets. Please note that the ' +
'"Secure Extra" and "Certificate" sections of ' +
'the database configuration are not present in export files, and ' +
'should be added manually after the import if they are needed.',
);
export const CONFIRM_OVERWRITE_MESSAGE = t(
'You are importing one or more datasets that already exist. ' +
'Overwriting might cause you to lose some of your work. Are you ' +
'sure you want to overwrite?',
);