From 7444429308f438fb2e29188db8f208223ed39128 Mon Sep 17 00:00:00 2001 From: Michal Klobukowski Date: Wed, 20 Jun 2018 09:56:12 +0200 Subject: [PATCH] Extend DataColumn and DataTable to handle sorting Feature #13633 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- .../data-explorer/data-explorer.tsx | 13 ++++++- src/components/data-table/data-column.ts | 15 +++++++- src/components/data-table/data-table.test.tsx | 36 +++++++++++++------ src/components/data-table/data-table.tsx | 15 +++++--- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx index 874c3259..ded1b5e3 100644 --- a/src/components/data-explorer/data-explorer.tsx +++ b/src/components/data-explorer/data-explorer.tsx @@ -3,10 +3,11 @@ // SPDX-License-Identifier: AGPL-3.0 import * as React from 'react'; -import { DataTable, DataColumn, ColumnSelector } from "../../components/data-table"; +import { DataTable, DataColumn, ColumnSelector, toggleSortDirection, SortDirection, resetSortDirection } from "../../components/data-table"; import { Typography, Grid, Paper, Toolbar } from '@material-ui/core'; import IconButton from '@material-ui/core/IconButton'; import MoreVertIcon from "@material-ui/icons/MoreVert"; +import TableSortLabel from '@material-ui/core/TableSortLabel'; import { formatFileSize, formatDate } from '../../common/formatters'; import { DataItem } from './data-item'; import { mockAnchorFromMouseEvent } from '../popover/helpers'; @@ -41,6 +42,8 @@ class DataExplorer extends React.Component columns: [{ name: "Name", selected: true, + sortDirection: "asc", + onSortToggle: () => this.toggleSort("Name"), render: item => this.renderName(item) }, { name: "Status", @@ -61,6 +64,7 @@ class DataExplorer extends React.Component }, { name: "Last modified", selected: true, + onSortToggle: () => this.toggleSort("Last modified"), render: item => renderDate(item.lastModified) }, { name: "Actions", @@ -184,6 +188,13 @@ class DataExplorer extends React.Component }; } + toggleSort = (columnName: string) => { + this.setState({ + columns: this.state.columns.map((column, index) => + column.name === columnName ? toggleSortDirection(column) : resetSortDirection(column)) + }); + } + } const renderIcon = (dataItem: DataItem) => { diff --git a/src/components/data-table/data-column.ts b/src/components/data-table/data-column.ts index d3b14736..f3d9576d 100644 --- a/src/components/data-table/data-column.ts +++ b/src/components/data-table/data-column.ts @@ -7,10 +7,23 @@ export interface DataColumn { selected: boolean; configurable?: boolean; key?: React.Key; + sortDirection?: SortDirection; + onSortToggle?: () => void; render: (item: T) => React.ReactElement; renderHeader?: () => React.ReactElement | null; } +export type SortDirection = "asc" | "desc"; + export const isColumnConfigurable = (column: DataColumn) => { return column.configurable === undefined || column.configurable; -}; \ No newline at end of file +}; + +export const toggleSortDirection = (column: DataColumn): DataColumn => { + const sortDirection = column.sortDirection === undefined || column.sortDirection === "desc" ? "asc" : "desc"; + return { ...column, sortDirection }; +}; + +export const resetSortDirection = (column: DataColumn): DataColumn => { + return { ...column, sortDirection: undefined }; +}; diff --git a/src/components/data-table/data-table.test.tsx b/src/components/data-table/data-table.test.tsx index 4a34a6b7..ad00280b 100644 --- a/src/components/data-table/data-table.test.tsx +++ b/src/components/data-table/data-table.test.tsx @@ -7,7 +7,7 @@ import { mount, configure } from "enzyme"; import * as Adapter from "enzyme-adapter-react-16"; import DataTable from "./data-table"; import { DataColumn } from "./data-column"; -import { TableHead, TableCell, Typography, TableBody, Button } from "@material-ui/core"; +import { TableHead, TableCell, Typography, TableBody, Button, TableSortLabel } from "@material-ui/core"; configure({ adapter: new Adapter() }); @@ -30,10 +30,10 @@ describe("", () => { selected: false } ]; - const dataTable = mount(); + const dataTable = mount(); expect(dataTable.find(TableHead).find(TableCell)).toHaveLength(2); }); - + it("renders column name", () => { const columns: Array> = [ { @@ -42,10 +42,10 @@ describe("", () => { selected: true } ]; - const dataTable = mount(); + const dataTable = mount(); expect(dataTable.find(TableHead).find(TableCell).text()).toBe("Column 1"); }); - + it("uses renderHeader instead of name prop", () => { const columns: Array> = [ { @@ -55,10 +55,10 @@ describe("", () => { selected: true } ]; - const dataTable = mount(); + const dataTable = mount(); expect(dataTable.find(TableHead).find(TableCell).text()).toBe("Column Header"); }); - + it("passes column key prop to corresponding cells", () => { const columns: Array> = [ { @@ -68,11 +68,11 @@ describe("", () => { selected: true } ]; - const dataTable = mount(); + const dataTable = mount(); expect(dataTable.find(TableHead).find(TableCell).key()).toBe("column-1-key"); expect(dataTable.find(TableBody).find(TableCell).key()).toBe("column-1-key"); }); - + it("shows information that items array is empty", () => { const columns: Array> = [ { @@ -81,7 +81,7 @@ describe("", () => { selected: true } ]; - const dataTable = mount(); + const dataTable = mount(); expect(dataTable.find(Typography).text()).toBe("No items"); }); @@ -98,10 +98,24 @@ describe("", () => { selected: true } ]; - const dataTable = mount(); + const dataTable = mount(); expect(dataTable.find(TableBody).find(Typography).text()).toBe("item 1"); expect(dataTable.find(TableBody).find(Button).text()).toBe("item 1"); }); + it("passes sorting props to ", () => { + const columns: Array> = [{ + name: "Column 1", + sortDirection: "asc", + onSortToggle: jest.fn(), + selected: true, + render: (item) => {item} + }]; + const dataTable = mount(); + expect(dataTable.find(TableSortLabel).prop("active")).toBeTruthy(); + dataTable.find(TableSortLabel).at(0).simulate("click"); + expect(columns[0].onSortToggle).toHaveBeenCalled(); + }); + }); \ No newline at end of file diff --git a/src/components/data-table/data-table.tsx b/src/components/data-table/data-table.tsx index ec260e96..565630be 100644 --- a/src/components/data-table/data-table.tsx +++ b/src/components/data-table/data-table.tsx @@ -3,7 +3,7 @@ // SPDX-License-Identifier: AGPL-3.0 import * as React from 'react'; -import { Table, TableBody, TableRow, TableCell, TableHead, StyleRulesCallback, Theme, WithStyles, withStyles, Typography } from '@material-ui/core'; +import { Table, TableBody, TableRow, TableCell, TableHead, TableSortLabel, StyleRulesCallback, Theme, WithStyles, withStyles, Typography } from '@material-ui/core'; import { DataColumn } from './data-column'; export interface DataTableProps { @@ -23,9 +23,16 @@ class DataTable extends React.Component & WithStyles {columns .filter(column => column.selected) - .map(({ name, renderHeader, key }, index) => + .map(({ name, renderHeader, key, sortDirection, onSortToggle }, index) => - {renderHeader ? renderHeader() : name} + {renderHeader ? + renderHeader() : + onSortToggle && onSortToggle()}> + {name} + } )} @@ -48,7 +55,7 @@ class DataTable extends React.Component & WithStyles )} - : : -- 2.30.2