refactor: Replace usages of reactable in TableLoader (#11240)

* Refactor TableLoader to use react-bootstrap

* Remove unnecessary css classes

* Fix import

* Fix styling for no data

* Refactor TableLoader into functional component

* Convert TableLoader to typescript
This commit is contained in:
Kamil Gabryjelski
2020-10-22 23:00:02 +02:00
committed by GitHub
parent 04ee4a2d08
commit dc5e5a978b
6 changed files with 139 additions and 123 deletions

View File

@@ -1,114 +0,0 @@
/**
* 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 React from 'react';
import PropTypes from 'prop-types';
import { Table, Tr, Td } from 'reactable-arc';
import { t, SupersetClient } from '@superset-ui/core';
import withToasts from '../messageToasts/enhancers/withToasts';
import Loading from './Loading';
import '../../stylesheets/reactable-pagination.less';
const propTypes = {
dataEndpoint: PropTypes.string.isRequired,
mutator: PropTypes.func,
columns: PropTypes.arrayOf(PropTypes.string),
addDangerToast: PropTypes.func.isRequired,
addInfoToast: PropTypes.func.isRequired,
addSuccessToast: PropTypes.func.isRequired,
addWarningToast: PropTypes.func.isRequired,
};
class TableLoader extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data: [],
};
}
UNSAFE_componentWillMount() {
const { dataEndpoint, mutator } = this.props;
SupersetClient.get({ endpoint: dataEndpoint })
.then(({ json }) => {
const data = mutator ? mutator(json) : json;
this.setState({ data, isLoading: false });
})
.catch(() => {
this.setState({ isLoading: false });
this.props.addDangerToast(t('An error occurred'));
});
}
render() {
if (this.state.isLoading) {
return <Loading />;
}
const {
addDangerToast,
addInfoToast,
addSuccessToast,
addWarningToast,
...tableProps
} = this.props;
let { columns } = this.props;
if (!columns && this.state.data.length > 0) {
columns = Object.keys(this.state.data[0]).filter(col => col[0] !== '_');
}
delete tableProps.dataEndpoint;
delete tableProps.mutator;
delete tableProps.columns;
return (
<Table
{...tableProps}
className="table"
itemsPerPage={50}
style={{ textTransform: 'capitalize' }}
>
{this.state.data.map((row, i) => (
<Tr key={i}>
{columns.map(col => {
if (row.hasOwnProperty(`_${col}`)) {
return (
<Td key={col} column={col} value={row[`_${col}`]}>
{row[col]}
</Td>
);
}
return (
<Td key={col} column={col}>
{row[col]}
</Td>
);
})}
</Tr>
))}
</Table>
);
}
}
TableLoader.propTypes = propTypes;
export default withToasts(TableLoader);

View File

@@ -0,0 +1,99 @@
/**
* 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 React, { useState, useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import { t, SupersetClient, JsonObject } from '@superset-ui/core';
import TableView from 'src/components/TableView';
import withToasts from '../messageToasts/enhancers/withToasts';
import Loading from './Loading';
import '../../stylesheets/reactable-pagination.less';
import { EmptyWrapperType } from './TableView/TableView';
const propTypes = {
dataEndpoint: PropTypes.string.isRequired,
mutator: PropTypes.func,
columns: PropTypes.arrayOf(PropTypes.string),
addDangerToast: PropTypes.func.isRequired,
addInfoToast: PropTypes.func.isRequired,
addSuccessToast: PropTypes.func.isRequired,
addWarningToast: PropTypes.func.isRequired,
};
interface TableLoaderProps {
dataEndpoint: string;
mutator(data: JsonObject): any[];
columns?: string[];
addDangerToast(text: string): any;
}
const TableLoader = (props: TableLoaderProps) => {
const [data, setData] = useState<Array<any>>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const { dataEndpoint, mutator } = props;
SupersetClient.get({ endpoint: dataEndpoint })
.then(({ json }) => {
const data = (mutator ? mutator(json) : json) as Array<any>;
setData(data);
setIsLoading(false);
})
.catch(() => {
setIsLoading(false);
props.addDangerToast(t('An error occurred'));
});
}, [props]);
const { columns, ...tableProps } = props;
const memoizedColumns = useMemo(() => {
let tableColumns = columns;
if (!columns && data.length > 0) {
tableColumns = Object.keys(data[0]).filter(col => col[0] !== '_');
}
return tableColumns
? tableColumns.map((column: string) => ({
accessor: column,
Header: column,
}))
: [];
}, [columns, data]);
delete tableProps.dataEndpoint;
delete tableProps.mutator;
if (isLoading) {
return <Loading />;
}
return (
<TableView
columns={memoizedColumns}
data={data}
pageSize={50}
loading={isLoading}
emptyWrapperType={EmptyWrapperType.Small}
{...tableProps}
/>
);
};
TableLoader.propTypes = propTypes;
export default withToasts(TableLoader);

View File

@@ -25,6 +25,11 @@ import { SortColumns } from './types';
const DEFAULT_PAGE_SIZE = 10;
export enum EmptyWrapperType {
Default,
Small,
}
export interface TableViewProps {
columns: any[];
data: any[];
@@ -33,6 +38,8 @@ export interface TableViewProps {
initialSortBy?: SortColumns;
loading?: boolean;
withPagination?: boolean;
emptyWrapperType?: EmptyWrapperType;
noDataText?: string;
className?: string;
}
@@ -65,6 +72,8 @@ const TableView = ({
initialSortBy = [],
loading = false,
withPagination = true,
emptyWrapperType = EmptyWrapperType.Default,
noDataText,
...props
}: TableViewProps) => {
const initialState = {
@@ -95,6 +104,21 @@ const TableView = ({
);
const content = withPagination ? page : rows;
let EmptyWrapperComponent;
switch (emptyWrapperType) {
case EmptyWrapperType.Small:
EmptyWrapperComponent = ({ children }: any) => <>{children}</>;
break;
case EmptyWrapperType.Default:
default:
EmptyWrapperComponent = ({ children }: any) => (
<EmptyWrapper>{children}</EmptyWrapper>
);
}
const isEmpty = !loading && content.length === 0;
return (
<TableViewStyles {...props}>
<TableCollection
@@ -106,10 +130,17 @@ const TableView = ({
columns={columns}
loading={loading}
/>
{!loading && content.length === 0 && (
<EmptyWrapper>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</EmptyWrapper>
{isEmpty && (
<EmptyWrapperComponent>
{noDataText ? (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={noDataText}
/>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</EmptyWrapperComponent>
)}
{pageCount > 1 && withPagination && (
<div className="pagination-container">

View File

@@ -39,7 +39,7 @@ class CreatedContent extends React.PureComponent<CreatedContentProps> {
return (
<TableLoader
dataEndpoint={`/superset/created_slices/${this.props.user.userId}/`}
className="table table-condensed"
className="table-condensed"
columns={['slice', 'favorited']}
mutator={mutator}
noDataText={t('No charts')}
@@ -57,7 +57,7 @@ class CreatedContent extends React.PureComponent<CreatedContentProps> {
}));
return (
<TableLoader
className="table table-condensed"
className="table-condensed"
mutator={mutator}
dataEndpoint={`/superset/created_dashboards/${this.props.user.userId}/`}
noDataText={t('No dashboards')}

View File

@@ -40,7 +40,7 @@ export default class Favorites extends React.PureComponent<FavoritesProps> {
return (
<TableLoader
dataEndpoint={`/superset/fave_slices/${this.props.user.userId}/`}
className="table table-condensed"
className="table-condensed"
columns={['slice', 'creator', 'favorited']}
mutator={mutator}
noDataText={t('No favorite charts yet, go click on stars!')}
@@ -58,7 +58,7 @@ export default class Favorites extends React.PureComponent<FavoritesProps> {
}));
return (
<TableLoader
className="table table-condensed"
className="table-condensed"
mutator={mutator}
dataEndpoint={`/superset/fave_dashboards/${this.props.user.userId}/`}
noDataText={t('No favorite dashboards yet, go click on stars!')}

View File

@@ -42,7 +42,7 @@ export default function RecentActivity({ user }: RecentActivityProps) {
return (
<div>
<TableLoader
className="table table-condensed"
className="table-condensed"
mutator={mutator}
sortable
dataEndpoint={`/superset/recent_activity/${user.userId}/?limit=${rowLimit}`}