You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ta...@apache.org on 2020/10/14 18:15:54 UTC
[incubator-superset] branch master updated: refactor: Implement
TableView component (#11217)
This is an automated email from the ASF dual-hosted git repository.
tai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push:
new 820fa47 refactor: Implement TableView component (#11217)
820fa47 is described below
commit 820fa473a7fc7571036b86a99ad66396d45e4c2f
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Wed Oct 14 20:15:34 2020 +0200
refactor: Implement TableView component (#11217)
---
.../components/AlteredSliceTag_spec.jsx | 2 +-
.../components/ListView/ListView_spec.jsx | 4 +-
.../components/TableView/TableView_spec.tsx | 143 +++++++++++++++++++++
.../src/components/ListView/ListView.tsx | 3 +-
superset-frontend/src/components/ListView/index.ts | 1 -
.../src/components/TableView/TableView.tsx | 137 ++++++++++++++++++++
.../components/{ListView => TableView}/index.ts | 6 +-
.../{ListView/index.ts => TableView/types.ts} | 9 +-
.../{ListView => dataViewCommon}/Pagination.tsx | 0
.../TableCollection.tsx | 11 +-
.../{ListView => dataViewCommon}/index.ts | 7 +-
11 files changed, 299 insertions(+), 24 deletions(-)
diff --git a/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx b/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx
index 8cb313c..41b4fe9 100644
--- a/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx
+++ b/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx
@@ -24,7 +24,7 @@ import AlteredSliceTag from 'src/components/AlteredSliceTag';
import ModalTrigger from 'src/components/ModalTrigger';
import TooltipWrapper from 'src/components/TooltipWrapper';
import ListView from 'src/components/ListView';
-import TableCollection from 'src/components/ListView/TableCollection';
+import TableCollection from 'src/components/dataViewCommon/TableCollection';
import {
defaultProps,
diff --git a/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx b/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx
index 118e01b..f8bed6b 100644
--- a/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx
+++ b/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx
@@ -29,9 +29,9 @@ import { CardSortSelect } from 'src/components/ListView/CardSortSelect';
import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox';
import ListView from 'src/components/ListView/ListView';
import ListViewFilters from 'src/components/ListView/Filters';
-import ListViewPagination from 'src/components/ListView/Pagination';
+import ListViewPagination from 'src/components/dataViewCommon/Pagination';
+import TableCollection from 'src/components/dataViewCommon/TableCollection';
import Pagination from 'src/components/Pagination';
-import TableCollection from 'src/components/ListView/TableCollection';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
diff --git a/superset-frontend/spec/javascripts/components/TableView/TableView_spec.tsx b/superset-frontend/spec/javascripts/components/TableView/TableView_spec.tsx
new file mode 100644
index 0000000..ee9b1ae
--- /dev/null
+++ b/superset-frontend/spec/javascripts/components/TableView/TableView_spec.tsx
@@ -0,0 +1,143 @@
+/**
+ * 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 { mount } from 'enzyme';
+import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import Pagination from 'src/components/Pagination';
+import TableView from '../../../../src/components/TableView';
+import { TableViewProps } from '../../../../src/components/TableView/TableView';
+
+const mockedProps: TableViewProps = {
+ columns: [
+ {
+ accessor: 'id',
+ Header: 'ID',
+ sortable: true,
+ },
+ {
+ accessor: 'age',
+ Header: 'Age',
+ },
+ {
+ accessor: 'name',
+ Header: 'Name',
+ },
+ ],
+ data: [
+ { id: 1, age: 20, name: 'Emily' },
+ { id: 2, age: 10, name: 'Kate' },
+ { id: 3, age: 40, name: 'Anna' },
+ { id: 4, age: 30, name: 'Jane' },
+ ],
+ pageSize: 1,
+};
+
+const factory = (props = mockedProps) =>
+ mount(<TableView {...props} />, {
+ wrappingComponent: ThemeProvider,
+ wrappingComponentProps: { theme: supersetTheme },
+ });
+
+describe('TableView', () => {
+ it('render a table, columns and rows', () => {
+ const pageSize = 10;
+ const wrapper = factory({ ...mockedProps, pageSize });
+ expect(wrapper.find('table')).toExist();
+ expect(wrapper.find('table th')).toHaveLength(mockedProps.columns.length);
+ expect(wrapper.find('table tbody tr')).toHaveLength(
+ Math.min(mockedProps.data.length, pageSize),
+ );
+ });
+
+ it('renders pagination controls', () => {
+ const wrapper = factory();
+ expect(wrapper.find(Pagination)).toExist();
+ expect(wrapper.find(Pagination.Prev)).toExist();
+ expect(wrapper.find(Pagination.Item)).toExist();
+ expect(wrapper.find(Pagination.Next)).toExist();
+ });
+
+ it("doesn't render pagination when pagination is disabled", () => {
+ const wrapper = factory({ ...mockedProps, withPagination: false });
+ expect(wrapper.find(Pagination)).not.toExist();
+ });
+
+ it("doesn't render pagination when fewer rows than page size", () => {
+ const pageSize = 999;
+ expect(pageSize).toBeGreaterThan(mockedProps.data.length);
+
+ const wrapper = factory({ ...mockedProps, pageSize });
+ expect(wrapper.find(Pagination)).not.toExist();
+ });
+
+ it('changes page when button is clicked', () => {
+ const pageSize = 3;
+ const dataLength = mockedProps.data.length;
+
+ expect(dataLength).toBeGreaterThan(pageSize);
+ const wrapper = factory({ ...mockedProps, pageSize });
+
+ expect(wrapper.find('table tbody tr')).toHaveLength(pageSize);
+
+ wrapper.find('NEXT_PAGE_LINK span').simulate('click');
+ expect(wrapper.find('table tbody tr')).toHaveLength(
+ Math.min(dataLength - pageSize, pageSize),
+ );
+ });
+
+ it('sorts by age when header cell is clicked', () => {
+ const wrapper = factory();
+ expect(wrapper.find('table tbody td Cell').at(1).props().value).toEqual(20);
+
+ // sort ascending
+ wrapper.find('table thead th').at(1).simulate('click');
+ expect(wrapper.find('table tbody td Cell').at(1).props().value).toEqual(10);
+
+ // sort descending
+ wrapper.find('table thead th').at(1).simulate('click');
+ expect(wrapper.find('table tbody td Cell').at(1).props().value).toEqual(40);
+
+ // no sort
+ wrapper.find('table thead th').at(1).simulate('click');
+ expect(wrapper.find('table tbody td Cell').at(1).props().value).toEqual(20);
+ });
+
+ it('sorts by data when initialSortBy is passed', () => {
+ let wrapper = factory();
+ expect(wrapper.find('table tbody td Cell').at(2).props().value).toEqual(
+ 'Emily',
+ );
+
+ wrapper = factory({
+ ...mockedProps,
+ initialSortBy: [{ id: 'name', desc: true }],
+ });
+ expect(wrapper.find('table tbody td Cell').at(2).props().value).toEqual(
+ 'Kate',
+ );
+
+ wrapper = factory({
+ ...mockedProps,
+ initialSortBy: [{ id: 'name', desc: false }],
+ });
+ expect(wrapper.find('table tbody td Cell').at(2).props().value).toEqual(
+ 'Anna',
+ );
+ });
+});
diff --git a/superset-frontend/src/components/ListView/ListView.tsx b/superset-frontend/src/components/ListView/ListView.tsx
index e79261c..e9e6581 100644
--- a/superset-frontend/src/components/ListView/ListView.tsx
+++ b/superset-frontend/src/components/ListView/ListView.tsx
@@ -24,9 +24,8 @@ import cx from 'classnames';
import Button from 'src/components/Button';
import Icon from 'src/components/Icon';
import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox';
-import TableCollection from './TableCollection';
+import { TableCollection, Pagination } from 'src/components/dataViewCommon';
import CardCollection from './CardCollection';
-import Pagination from './Pagination';
import FilterControls from './Filters';
import { CardSortSelect } from './CardSortSelect';
import {
diff --git a/superset-frontend/src/components/ListView/index.ts b/superset-frontend/src/components/ListView/index.ts
index 30be8a1..0d6753f 100644
--- a/superset-frontend/src/components/ListView/index.ts
+++ b/superset-frontend/src/components/ListView/index.ts
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
export * from './ListView';
export * from './types';
diff --git a/superset-frontend/src/components/TableView/TableView.tsx b/superset-frontend/src/components/TableView/TableView.tsx
new file mode 100644
index 0000000..3eb11a8
--- /dev/null
+++ b/superset-frontend/src/components/TableView/TableView.tsx
@@ -0,0 +1,137 @@
+/**
+ * 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 { 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';
+
+const DEFAULT_PAGE_SIZE = 10;
+
+export interface TableViewProps {
+ columns: any[];
+ data: any[];
+ pageSize?: number;
+ initialPageIndex?: number;
+ initialSortBy?: SortColumns;
+ loading?: boolean;
+ withPagination?: boolean;
+ className?: string;
+}
+
+const EmptyWrapper = styled.div`
+ margin: ${({ theme }) => theme.gridUnit * 40}px 0;
+`;
+
+const TableViewStyles = styled.div`
+ .table-cell.table-cell {
+ vertical-align: top;
+ }
+
+ .pagination-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+
+ .row-count-container {
+ margin-top: ${({ theme }) => theme.gridUnit * 2}px;
+ color: ${({ theme }) => theme.colors.grayscale.base};
+ }
+`;
+
+const TableView = ({
+ columns,
+ data,
+ pageSize: initialPageSize,
+ initialPageIndex,
+ initialSortBy = [],
+ loading = false,
+ withPagination = true,
+ ...props
+}: TableViewProps) => {
+ const initialState = {
+ pageSize: initialPageSize ?? DEFAULT_PAGE_SIZE,
+ pageIndex: initialPageIndex ?? 0,
+ sortBy: initialSortBy,
+ };
+
+ const {
+ getTableProps,
+ getTableBodyProps,
+ headerGroups,
+ page,
+ rows,
+ prepareRow,
+ pageCount,
+ gotoPage,
+ state: { pageIndex, pageSize },
+ } = useTable(
+ {
+ columns,
+ data,
+ initialState,
+ },
+ useFilters,
+ useSortBy,
+ usePagination,
+ );
+
+ const content = withPagination ? page : rows;
+ return (
+ <TableViewStyles {...props}>
+ <TableCollection
+ getTableProps={getTableProps}
+ getTableBodyProps={getTableBodyProps}
+ prepareRow={prepareRow}
+ headerGroups={headerGroups}
+ rows={content}
+ columns={columns}
+ loading={loading}
+ />
+ {!loading && content.length === 0 && (
+ <EmptyWrapper>
+ <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+ </EmptyWrapper>
+ )}
+ {pageCount > 1 && withPagination && (
+ <div className="pagination-container">
+ <Pagination
+ totalPages={pageCount || 0}
+ currentPage={pageCount ? pageIndex + 1 : 0}
+ onChange={(p: number) => gotoPage(p - 1)}
+ hideFirstAndLastPageLinks
+ />
+ <div className="row-count-container">
+ {!loading &&
+ t(
+ '%s-%s of %s',
+ pageSize * pageIndex + (page.length && 1),
+ pageSize * pageIndex + page.length,
+ data.length,
+ )}
+ </div>
+ </div>
+ )}
+ </TableViewStyles>
+ );
+};
+
+export default TableView;
diff --git a/superset-frontend/src/components/ListView/index.ts b/superset-frontend/src/components/TableView/index.ts
similarity index 89%
copy from superset-frontend/src/components/ListView/index.ts
copy to superset-frontend/src/components/TableView/index.ts
index 30be8a1..88d57c4 100644
--- a/superset-frontend/src/components/ListView/index.ts
+++ b/superset-frontend/src/components/TableView/index.ts
@@ -16,8 +16,4 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-export * from './ListView';
-export * from './types';
-
-export { default } from './ListView';
+export { default } from './TableView';
diff --git a/superset-frontend/src/components/ListView/index.ts b/superset-frontend/src/components/TableView/types.ts
similarity index 88%
copy from superset-frontend/src/components/ListView/index.ts
copy to superset-frontend/src/components/TableView/types.ts
index 30be8a1..974cb24 100644
--- a/superset-frontend/src/components/ListView/index.ts
+++ b/superset-frontend/src/components/TableView/types.ts
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
+export interface SortColumn {
+ id: string;
+ desc?: boolean;
+}
-export * from './ListView';
-export * from './types';
-
-export { default } from './ListView';
+export type SortColumns = SortColumn[];
diff --git a/superset-frontend/src/components/ListView/Pagination.tsx b/superset-frontend/src/components/dataViewCommon/Pagination.tsx
similarity index 100%
rename from superset-frontend/src/components/ListView/Pagination.tsx
rename to superset-frontend/src/components/dataViewCommon/Pagination.tsx
diff --git a/superset-frontend/src/components/ListView/TableCollection.tsx b/superset-frontend/src/components/dataViewCommon/TableCollection.tsx
similarity index 96%
rename from superset-frontend/src/components/ListView/TableCollection.tsx
rename to superset-frontend/src/components/dataViewCommon/TableCollection.tsx
index db79937..eb63825 100644
--- a/superset-frontend/src/components/ListView/TableCollection.tsx
+++ b/superset-frontend/src/components/dataViewCommon/TableCollection.tsx
@@ -33,8 +33,7 @@ interface TableCollectionProps {
highlightRowId?: number;
}
-const Table = styled.table`
- background-color: white;
+export const Table = styled.table`
border-collapse: separate;
border-radius: ${({ theme }) => theme.borderRadius}px;
@@ -199,6 +198,8 @@ const Table = styled.table`
}
`;
+Table.displayName = 'table';
+
export default function TableCollection({
getTableProps,
getTableBodyProps,
@@ -267,13 +268,15 @@ export default function TableCollection({
{rows.length > 0 &&
rows.map(row => {
prepareRow(row);
+ // @ts-ignore
+ const rowId = row.original.id;
return (
<tr
{...row.getRowProps()}
className={cx('table-row', {
'table-row-selected':
- // @ts-ignore
- row.isSelected || row.original.id === highlightRowId,
+ row.isSelected ||
+ (typeof rowId !== 'undefined' && rowId === highlightRowId),
})}
>
{row.cells.map(cell => {
diff --git a/superset-frontend/src/components/ListView/index.ts b/superset-frontend/src/components/dataViewCommon/index.ts
similarity index 87%
copy from superset-frontend/src/components/ListView/index.ts
copy to superset-frontend/src/components/dataViewCommon/index.ts
index 30be8a1..e552e5c 100644
--- a/superset-frontend/src/components/ListView/index.ts
+++ b/superset-frontend/src/components/dataViewCommon/index.ts
@@ -16,8 +16,5 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-export * from './ListView';
-export * from './types';
-
-export { default } from './ListView';
+export { default as Pagination } from './Pagination';
+export { default as TableCollection } from './TableCollection';