[ARVADOS-WORKBENCH2] updated: 1.1.4-121-g5925c0e

Git user git at public.curoverse.com
Wed Jun 20 07:28:46 EDT 2018


Summary of changes:
 package.json                                       |   4 +-
 .../data-table-filters/data-table-filters.test.tsx |  70 ++++++++
 .../data-table-filters/data-table-filters.tsx      | 189 +++++++++++++++++++++
 src/components/data-table/data-column.ts           |   4 +
 src/components/data-table/data-table.tsx           |  22 ++-
 .../data-explorer/data-explorer.tsx                |  16 ++
 yarn.lock                                          |  20 ++-
 7 files changed, 314 insertions(+), 11 deletions(-)
 create mode 100644 src/components/data-table-filters/data-table-filters.test.tsx
 create mode 100644 src/components/data-table-filters/data-table-filters.tsx

       via  5925c0eb9468ffca9419e47ae333501968b4e24e (commit)
       via  fd79729916cf6ddf27063b1865d39a36abb9e038 (commit)
      from  1a9eb2261e6030ba78078e2a206bad27653f2475 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 5925c0eb9468ffca9419e47ae333501968b4e24e
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Jun 20 13:28:28 2018 +0200

    Create tests for data table filter
    
    Feature #13633
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/components/data-table-filters/data-table-filters.test.tsx b/src/components/data-table-filters/data-table-filters.test.tsx
new file mode 100644
index 0000000..6756252
--- /dev/null
+++ b/src/components/data-table-filters/data-table-filters.test.tsx
@@ -0,0 +1,70 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { mount, configure } from "enzyme";
+import DataTableFilter, { DataTableFilterItem } from "./data-table-filters";
+
+import * as Adapter from 'enzyme-adapter-react-16';
+import { Checkbox, ButtonBase, ListItem, Button, ListItemText } from "@material-ui/core";
+import dataTableFilters from "./data-table-filters";
+
+configure({ adapter: new Adapter() });
+
+describe("<DataTableFilter />", () => {
+    it("renders filters according to their state", () => {
+        const filters = [{
+            name: "Filter 1",
+            selected: true
+        }, {
+            name: "Filter 2",
+            selected: false
+        }];
+        const dataTableFilter = mount(<DataTableFilter name="" filters={filters} />);
+        dataTableFilter.find(ButtonBase).simulate("click");
+        expect(dataTableFilter.find(Checkbox).at(0).prop("checked")).toBeTruthy();
+        expect(dataTableFilter.find(Checkbox).at(1).prop("checked")).toBeFalsy();
+    });
+    
+    it("updates filters after filters prop change", () => {
+        const filters = [{
+            name: "Filter 1",
+            selected: true
+        }];
+        const updatedFilters = [, {
+            name: "Filter 2",
+            selected: true
+        }];
+        const dataTableFilter = mount(<DataTableFilter name="" filters={filters} />);
+        dataTableFilter.find(ButtonBase).simulate("click");
+        expect(dataTableFilter.find(Checkbox).prop("checked")).toBeTruthy();
+        dataTableFilter.find(ListItem).simulate("click");
+        expect(dataTableFilter.find(Checkbox).prop("checked")).toBeFalsy();
+        dataTableFilter.setProps({filters: updatedFilters});
+        expect(dataTableFilter.find(Checkbox).prop("checked")).toBeTruthy();
+        expect(dataTableFilter.find(ListItemText).text()).toBe("Filter 2");
+    });
+
+    it("calls onChange with modified list of filters", () => {
+        const filters = [{
+            name: "Filter 1",
+            selected: true
+        }, {
+            name: "Filter 2",
+            selected: false
+        }];
+        const onChange = jest.fn();
+        const dataTableFilter = mount(<DataTableFilter name="" filters={filters} onChange={onChange} />);
+        dataTableFilter.find(ButtonBase).simulate("click");
+        dataTableFilter.find(ListItem).at(1).simulate("click");
+        dataTableFilter.find(Button).at(0).simulate("click");
+        expect(onChange).toHaveBeenCalledWith([{
+            name: "Filter 1",
+            selected: true
+        }, {
+            name: "Filter 2",
+            selected: true
+        }]);
+    });
+});
\ No newline at end of file

commit fd79729916cf6ddf27063b1865d39a36abb9e038
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Jun 20 12:59:11 2018 +0200

    Create data table filter component
    
    Feature #13633
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/package.json b/package.json
index fda2ead..1f08e6c 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
     "@material-ui/icons": "1.1.0",
     "@types/lodash": "4.14.109",
     "axios": "0.18.0",
+    "classnames": "^2.2.6",
     "lodash": "4.17.10",
     "react": "16.4.1",
     "react-dom": "16.4.1",
@@ -27,11 +28,12 @@
     "lint": "tslint src/** -t verbose"
   },
   "devDependencies": {
+    "@types/classnames": "^2.2.4",
     "@types/enzyme": "3.1.10",
     "@types/enzyme-adapter-react-16": "1.0.2",
     "@types/jest": "23.1.0",
     "@types/node": "10.3.3",
-    "@types/react": "16.3.18",
+    "@types/react": "16.4.1",
     "@types/react-dom": "16.0.6",
     "@types/react-redux": "6.0.2",
     "@types/react-router": "4.0.26",
diff --git a/src/components/data-table-filters/data-table-filters.tsx b/src/components/data-table-filters/data-table-filters.tsx
new file mode 100644
index 0000000..cf0260b
--- /dev/null
+++ b/src/components/data-table-filters/data-table-filters.tsx
@@ -0,0 +1,189 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import {
+    WithStyles,
+    withStyles,
+    ButtonBase,
+    StyleRulesCallback,
+    Theme,
+    Popover,
+    List,
+    ListItem,
+    Checkbox,
+    ListItemText,
+    Button,
+    Card,
+    CardActions,
+    Typography,
+    CardContent
+} from "@material-ui/core";
+import * as classnames from "classnames";
+import { DefaultTransformOrigin } from "../popover/helpers";
+
+export interface DataTableFilterItem {
+    name: string;
+    selected: boolean;
+}
+
+export interface DataTableFilterProps {
+    name: string;
+    filters: DataTableFilterItem[];
+    onChange?: (filters: DataTableFilterItem[]) => void;
+}
+
+interface DataTableFilterState {
+    anchorEl?: HTMLElement;
+    filters: DataTableFilterItem[];
+    prevFilters: DataTableFilterItem[];
+}
+
+class DataTableFilter extends React.Component<DataTableFilterProps & WithStyles<CssRules>, DataTableFilterState> {
+    state: DataTableFilterState = {
+        anchorEl: undefined,
+        filters: [],
+        prevFilters: []
+    };
+    icon = React.createRef<HTMLElement>();
+
+    render() {
+        const { name, classes, children } = this.props;
+        return <>
+            <ButtonBase
+                className={classnames([
+                    classes.root,
+                    { [classes.active]: this.state.filters.filter(({ selected }) => !selected).length > 0 }])}
+                component="span"
+                onClick={this.open}
+                disableRipple>
+                {children}
+                <i className={classnames(["fas fa-filter", classes.icon])}
+                    data-fa-transform="shrink-3"
+                    ref={this.icon} />
+            </ButtonBase>
+            <Popover
+                anchorEl={this.state.anchorEl}
+                open={!!this.state.anchorEl}
+                anchorOrigin={DefaultTransformOrigin}
+                transformOrigin={DefaultTransformOrigin}
+                onClose={this.cancel}>
+                <Card>
+                    <CardContent>
+                        <Typography variant="caption">
+                            {name}
+                        </Typography>
+                    </CardContent>
+                    <List dense>
+                        {this.state.filters.map((filter, index) =>
+                            <ListItem
+                                button
+                                key={index}
+                                onClick={this.toggleFilter(filter)}>
+                                <Checkbox
+                                    disableRipple
+                                    color="primary"
+                                    checked={filter.selected}
+                                    className={classes.checkbox} />
+                                <ListItemText>
+                                    {filter.name}
+                                </ListItemText>
+                            </ListItem>
+                        )}
+                    </List>
+                    <CardActions>
+                        <Button
+                            color="primary"
+                            variant="raised"
+                            size="small"
+                            onClick={this.submit}>
+                            Ok
+                        </Button>
+                        <Button
+                            color="primary"
+                            variant="outlined"
+                            size="small"
+                            onClick={this.cancel}>
+                            Cancel
+                        </Button>
+                    </CardActions >
+                </Card>
+            </Popover>
+        </>;
+    }
+
+    static getDerivedStateFromProps(props: DataTableFilterProps, state: DataTableFilterState): DataTableFilterState {
+        return props.filters !== state.prevFilters
+            ? { ...state, filters: props.filters, prevFilters: props.filters }
+            : state;
+    }
+
+    open = () => {
+        this.setState({ anchorEl: this.icon.current || undefined });
+    }
+
+    submit = () => {
+        const { onChange } = this.props;
+        if (onChange) {
+            onChange(this.state.filters);
+        }
+        this.setState({ anchorEl: undefined });
+    }
+
+    cancel = () => {
+        this.setState(prev => ({
+            ...prev,
+            filters: prev.prevFilters,
+            anchorEl: undefined
+        }));
+    }
+
+    toggleFilter = (toggledFilter: DataTableFilterItem) => () => {
+        this.setState(prev => ({
+            ...prev,
+            filters: prev.filters.map(filter =>
+                filter === toggledFilter
+                    ? { ...filter, selected: !filter.selected }
+                    : filter)
+        }));
+    }
+}
+
+
+export type CssRules = "root" | "icon" | "active" | "checkbox";
+
+const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
+    root: {
+        cursor: "pointer",
+        display: "inline-flex",
+        justifyContent: "flex-start",
+        flexDirection: "inherit",
+        alignItems: "center",
+        "&:hover": {
+            color: theme.palette.text.primary,
+        },
+        "&:focus": {
+            color: theme.palette.text.primary,
+        },
+    },
+    active: {
+        color: theme.palette.text.primary,
+        '& $icon': {
+            opacity: 1,
+        },
+    },
+    icon: {
+        marginRight: 4,
+        marginLeft: 4,
+        opacity: 0.7,
+        userSelect: "none",
+        width: 16
+    },
+    checkbox: {
+        width: 24,
+        height: 24
+    }
+});
+
+export default withStyles(styles)(DataTableFilter);
diff --git a/src/components/data-table/data-column.ts b/src/components/data-table/data-column.ts
index f3d9576..7ac568a 100644
--- a/src/components/data-table/data-column.ts
+++ b/src/components/data-table/data-column.ts
@@ -2,6 +2,8 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
+import { DataTableFilterItem } from "../data-table-filters/data-table-filters";
+
 export interface DataColumn<T> {
     name: string;
     selected: boolean;
@@ -9,6 +11,8 @@ export interface DataColumn<T> {
     key?: React.Key;
     sortDirection?: SortDirection;
     onSortToggle?: () => void;
+    filters?: DataTableFilterItem[];
+    onFiltersChange?: (filters: DataTableFilterItem[]) => void;
     render: (item: T) => React.ReactElement<void>;
     renderHeader?: () => React.ReactElement<void> | null;
 }
diff --git a/src/components/data-table/data-table.tsx b/src/components/data-table/data-table.tsx
index 43efdd0..26f6faf 100644
--- a/src/components/data-table/data-table.tsx
+++ b/src/components/data-table/data-table.tsx
@@ -5,6 +5,7 @@
 import * as React from 'react';
 import { Table, TableBody, TableRow, TableCell, TableHead, TableSortLabel, StyleRulesCallback, Theme, WithStyles, withStyles, Typography } from '@material-ui/core';
 import { DataColumn } from './data-column';
+import DataTableFilters from "../data-table-filters/data-table-filters";
 
 export type DataColumns<T> = Array<DataColumn<T>>;
 
@@ -25,16 +26,23 @@ class DataTable<T> extends React.Component<DataTableProps<T> & WithStyles<CssRul
                         <TableRow>
                             {columns
                                 .filter(column => column.selected)
-                                .map(({ name, renderHeader, key, sortDirection, onSortToggle }, index) =>
+                                .map(({ name, renderHeader, key, sortDirection, onSortToggle, filters, onFiltersChange }, index) =>
                                     <TableCell key={key || index}>
                                         {renderHeader ?
                                             renderHeader() :
-                                            <TableSortLabel
-                                                active={!!sortDirection}
-                                                direction={sortDirection}
-                                                onClick={() => onSortToggle && onSortToggle()}>
-                                                {name}
-                                            </TableSortLabel>}
+                                            filters ?
+                                                <DataTableFilters
+                                                    name={`${name} filters`}
+                                                    onChange={onFiltersChange}
+                                                    filters={filters}>
+                                                    {name}
+                                                </DataTableFilters> :
+                                                <TableSortLabel
+                                                    active={!!sortDirection}
+                                                    direction={sortDirection}
+                                                    onClick={() => onSortToggle && onSortToggle()}>
+                                                    {name}
+                                                </TableSortLabel>}
                                     </TableCell>
                                 )}
                         </TableRow>
diff --git a/src/views-components/data-explorer/data-explorer.tsx b/src/views-components/data-explorer/data-explorer.tsx
index ffb21f9..dab686e 100644
--- a/src/views-components/data-explorer/data-explorer.tsx
+++ b/src/views-components/data-explorer/data-explorer.tsx
@@ -49,10 +49,26 @@ class DataExplorer extends React.Component<DataExplorerProps, DataExplorerState>
         }, {
             name: "Status",
             selected: true,
+            onFiltersChange: console.log,
+            filters: [{
+                name: "In progress", 
+                selected: true
+            }, {
+                name: "Complete",
+                selected: true
+            }],
             render: item => renderStatus(item.status)
         }, {
             name: "Type",
             selected: true,
+            onFiltersChange: console.log,
+            filters: [{
+                name: "Collection", 
+                selected: true
+            }, {
+                name: "Group",
+                selected: true
+            }],
             render: item => renderType(item.type)
         }, {
             name: "Owner",
diff --git a/yarn.lock b/yarn.lock
index eee6c86..05a6ef1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -65,6 +65,10 @@
   version "0.22.7"
   resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.7.tgz#4a92eafedfb2b9f4437d3a4410006d81114c66ce"
 
+"@types/classnames@^2.2.4":
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.4.tgz#d3ee9ebf714aa34006707b8f4a58fd46b642305a"
+
 "@types/enzyme-adapter-react-16 at 1.0.2":
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.2.tgz#15ae37c64d6221a6f4b3a4aacc357cf773859de4"
@@ -145,12 +149,18 @@
   dependencies:
     "@types/react" "*"
 
-"@types/react@*", "@types/react at 16.3.18":
+"@types/react@*":
   version "16.3.18"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.18.tgz#bf195aed4d77dc86f06e4c9bb760214a3b822b8d"
   dependencies:
     csstype "^2.2.0"
 
+"@types/react at 16.4.1":
+  version "16.4.1"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.1.tgz#c53bbfb4a78933db587da085ac60dbf5fcf73f8f"
+  dependencies:
+    csstype "^2.2.0"
+
 "@types/redux-devtools at 3.0.44":
   version "3.0.44"
   resolved "https://registry.yarnpkg.com/@types/redux-devtools/-/redux-devtools-3.0.44.tgz#2781b87067b8aec3102d4cb4a478feb340df5259"
@@ -1559,7 +1569,7 @@ class-utils@^0.3.5:
     isobject "^3.0.0"
     static-extend "^0.1.1"
 
-classnames@^2.2.5:
+classnames@^2.2.5, classnames@^2.2.6:
   version "2.2.6"
   resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
 
@@ -2007,10 +2017,14 @@ cssom at 0.3.x, "cssom@>= 0.3.2 < 0.4.0":
   dependencies:
     cssom "0.3.x"
 
-csstype@^2.0.0, csstype@^2.2.0, csstype@^2.5.2:
+csstype@^2.0.0, csstype@^2.5.2:
   version "2.5.3"
   resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.3.tgz#2504152e6e1cc59b32098b7f5d6a63f16294c1f7"
 
+csstype@^2.2.0:
+  version "2.5.5"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.5.tgz#4125484a3d42189a863943f23b9e4b80fedfa106"
+
 currently-unhandled@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list