Update data-explorer to use table filters tree
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Wed, 21 Nov 2018 08:52:34 +0000 (09:52 +0100)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Wed, 21 Nov 2018 08:52:34 +0000 (09:52 +0100)
Feature #14258

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

src/components/data-explorer/data-explorer.tsx
src/components/data-table/data-column.ts
src/components/data-table/data-table.tsx
src/models/tree.ts
src/store/data-explorer/data-explorer-action.ts
src/store/data-explorer/data-explorer-middleware-service.ts
src/store/data-explorer/data-explorer-middleware.test.ts
src/store/data-explorer/data-explorer-reducer.ts

index 08f52d00d37663f321106be226cea10c81ef08f0..c9009c8a802033707325ec03967005c58211d634 100644 (file)
@@ -8,9 +8,10 @@ import MoreVertIcon from "@material-ui/icons/MoreVert";
 import { ColumnSelector } from "../column-selector/column-selector";
 import { DataTable, DataColumns } from "../data-table/data-table";
 import { DataColumn, SortDirection } from "../data-table/data-column";
-import { DataTableFilterItem } from '../data-table-filters/data-table-filters';
 import { SearchInput } from '../search-input/search-input';
 import { ArvadosTheme } from "~/common/custom-theme";
+import { createTree } from '~/models/tree';
+import { DataTableFilters } from '../data-table-filters/data-table-filters-tree';
 
 type CssRules = 'searchBox' | "toolbar" | "footer" | "root" | 'moreOptionsButton';
 
@@ -53,7 +54,7 @@ interface DataExplorerActionProps<T> {
     onColumnToggle: (column: DataColumn<T>) => void;
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: T) => void;
     onSortToggle: (column: DataColumn<T>) => void;
-    onFiltersChange: (filters: DataTableFilterItem[], column: DataColumn<T>) => void;
+    onFiltersChange: (filters: DataTableFilters, column: DataColumn<T>) => void;
     onChangePage: (page: number) => void;
     onChangeRowsPerPage: (rowsPerPage: number) => void;
     extractKey?: (item: T) => React.Key;
@@ -137,7 +138,7 @@ export const DataExplorer = withStyles(styles)(
             selected: true,
             configurable: false,
             sortDirection: SortDirection.NONE,
-            filters: [],
+            filters: createTree(),
             key: "context-actions",
             render: this.renderContextMenuTrigger
         };
index a5f95506a816da0decfd4754705ea4cb8c0cddc6..28e93beed0cebadc1fbaa54324cc2729a3a86ed5 100644 (file)
@@ -3,15 +3,16 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from "react";
-import { DataTableFilterItem } from "../data-table-filters/data-table-filters";
+import { DataTableFilters } from "../data-table-filters/data-table-filters-tree";
+import { createTree } from '~/models/tree';
 
-export interface DataColumn<T, F extends DataTableFilterItem = DataTableFilterItem> {
+export interface DataColumn<T> {
     key?: React.Key;
     name: string;
     selected: boolean;
     configurable: boolean;
     sortDirection?: SortDirection;
-    filters: F[];
+    filters: DataTableFilters;
     render: (item: T) => React.ReactElement<any>;
     renderHeader?: () => React.ReactElement<any>;
 }
@@ -34,13 +35,13 @@ export const resetSortDirection = <T>(column: DataColumn<T>): DataColumn<T> => {
     return column.sortDirection ? { ...column, sortDirection: SortDirection.NONE } : column;
 };
 
-export const createDataColumn = <T, F extends DataTableFilterItem>(dataColumn: Partial<DataColumn<T, F>>): DataColumn<T, F> => ({
+export const createDataColumn = <T>(dataColumn: Partial<DataColumn<T>>): DataColumn<T> => ({
     key: '',
     name: '',
     selected: true,
     configurable: true,
     sortDirection: SortDirection.NONE,
-    filters: [],
+    filters: createTree(),
     render: () => React.createElement('span'),
     ...dataColumn,
 });
index 25d81c62fa7dc89a8135c8a718c5dc02800d22a6..d9157a6a5d0e14851f141309c8a89f96af818c50 100644 (file)
@@ -5,10 +5,12 @@
 import * as React from 'react';
 import { Table, TableBody, TableRow, TableCell, TableHead, TableSortLabel, StyleRulesCallback, Theme, WithStyles, withStyles } from '@material-ui/core';
 import { DataColumn, SortDirection } from './data-column';
-import { DataTableFilters, DataTableFilterItem } from "../data-table-filters/data-table-filters";
 import { DataTableDefaultView } from '../data-table-default-view/data-table-default-view';
+import { DataTableFilters } from '../data-table-filters/data-table-filters-tree';
+import { DataTableFiltersPopover } from '../data-table-filters/data-table-filters-popover';
+import { countNodes } from '~/models/tree';
 
-export type DataColumns<T, F extends DataTableFilterItem = DataTableFilterItem> = Array<DataColumn<T, F>>;
+export type DataColumns<T> = Array<DataColumn<T>>;
 
 export interface DataTableDataProps<T> {
     items: T[];
@@ -17,7 +19,7 @@ export interface DataTableDataProps<T> {
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: T) => void;
     onRowDoubleClick: (event: React.MouseEvent<HTMLTableRowElement>, item: T) => void;
     onSortToggle: (column: DataColumn<T>) => void;
-    onFiltersChange: (filters: DataTableFilterItem[], column: DataColumn<T>) => void;
+    onFiltersChange: (filters: DataTableFilters, column: DataColumn<T>) => void;
     extractKey?: (item: T) => React.Key;
     working?: boolean;
     defaultView?: React.ReactNode;
@@ -81,15 +83,15 @@ export const DataTable = withStyles(styles)(
             return <TableCell key={key || index}>
                 {renderHeader ?
                     renderHeader() :
-                    filters.length > 0
-                        ? <DataTableFilters
+                    countNodes(filters) > 0
+                        ? <DataTableFiltersPopover
                             name={`${name} filters`}
                             onChange={filters =>
                                 onFiltersChange &&
                                 onFiltersChange(filters, column)}
                             filters={filters}>
                             {name}
-                        </DataTableFilters>
+                        </DataTableFiltersPopover>
                         : sortDirection
                             ? <TableSortLabel
                                 active={sortDirection !== SortDirection.NONE}
index bdcd7309a1304d27e27ac1b31c464203fc0e83af..1bc15bc0824a500c1121812c252c82eaa2fc7ca0 100644 (file)
@@ -95,6 +95,9 @@ export const getNodeAncestorsIds = (id: string) => <T>(tree: Tree<T>): string[]
 export const getNodeDescendants = (id: string, limit = Infinity) => <T>(tree: Tree<T>) =>
     mapIdsToNodes(getNodeDescendantsIds(id, limit)(tree))(tree);
 
+export const countNodes = <T>(tree: Tree<T>) =>
+    getNodeDescendantsIds('')(tree).length;
+
 export const getNodeDescendantsIds = (id: string, limit = Infinity) => <T>(tree: Tree<T>): string[] => {
     const node = getNode(id)(tree);
     const children = node ? node.children :
index a58d20edb17eea49270caf32a5cc959bef7ecb82..7797ae6cefc27759d3f994b0d7f14f9225b5f193 100644 (file)
@@ -3,14 +3,14 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { unionize, ofType, UnionOf } from "~/common/unionize";
-import { DataTableFilterItem } from "~/components/data-table-filters/data-table-filters";
 import { DataColumns } from "~/components/data-table/data-table";
+import { DataTableFilters } from '~/components/data-table-filters/data-table-filters-tree';
 
 export const dataExplorerActions = unionize({
     RESET_PAGINATION: ofType<{ id: string }>(),
     REQUEST_ITEMS: ofType<{ id: string }>(),
     SET_COLUMNS: ofType<{ id: string, columns: DataColumns<any> }>(),
-    SET_FILTERS: ofType<{ id: string, columnName: string, filters: DataTableFilterItem[] }>(),
+    SET_FILTERS: ofType<{ id: string, columnName: string, filters: DataTableFilters }>(),
     SET_ITEMS: ofType<{ id: string, items: any[], page: number, rowsPerPage: number, itemsAvailable: number }>(),
     SET_PAGE: ofType<{ id: string, page: number }>(),
     SET_ROWS_PER_PAGE: ofType<{ id: string, rowsPerPage: number }>(),
@@ -28,7 +28,7 @@ export const bindDataExplorerActions = (id: string) => ({
         dataExplorerActions.REQUEST_ITEMS({ id }),
     SET_COLUMNS: (payload: { columns: DataColumns<any> }) =>
         dataExplorerActions.SET_COLUMNS({ ...payload, id }),
-    SET_FILTERS: (payload: { columnName: string, filters: DataTableFilterItem[] }) =>
+    SET_FILTERS: (payload: { columnName: string, filters: DataTableFilters }) =>
         dataExplorerActions.SET_FILTERS({ ...payload, id }),
     SET_ITEMS: (payload: { items: any[], page: number, rowsPerPage: number, itemsAvailable: number }) =>
         dataExplorerActions.SET_ITEMS({ ...payload, id }),
index 934af7be94ca04ebe369a7911403c09810592684..80ab514cb41440b9a3356f62ed6379a4ec08896c 100644 (file)
@@ -5,9 +5,10 @@
 import { Dispatch, MiddlewareAPI } from "redux";
 import { RootState } from "../store";
 import { DataColumns } from "~/components/data-table/data-table";
-import { DataTableFilterItem } from "~/components/data-table-filters/data-table-filters";
 import { DataExplorer } from './data-explorer-reducer';
 import { ListResults } from '~/services/common-service/common-resource-service';
+import { createTree } from "~/models/tree";
+import { DataTableFilters } from "~/components/data-table-filters/data-table-filters-tree";
 
 export abstract class DataExplorerMiddlewareService {
     protected readonly id: string;
@@ -20,20 +21,19 @@ export abstract class DataExplorerMiddlewareService {
         return this.id;
     }
 
-    public getColumnFilters<T, F extends DataTableFilterItem>(columns: DataColumns<T, F>, columnName: string): F[] {
-        const column = columns.find(c => c.name === columnName);
-        return column ? column.filters.filter(f => f.selected) : [];
+    public getColumnFilters<T>(columns: DataColumns<T>, columnName: string): DataTableFilters {
+        return getDataExplorerColumnFilters(columns, columnName);
     }
 
     abstract requestItems(api: MiddlewareAPI<Dispatch, RootState>): void;
 }
 
-export const getDataExplorerColumnFilters = <T, F extends DataTableFilterItem>(columns: DataColumns<T, F>, columnName: string): F[] => {
+export const getDataExplorerColumnFilters = <T>(columns: DataColumns<T>, columnName: string): DataTableFilters => {
     const column = columns.find(c => c.name === columnName);
-    return column ? column.filters.filter(f => f.selected) : [];
+    return column ? column.filters : createTree();
 };
 
-export const dataExplorerToListParams = <R>(dataExplorer: DataExplorer) => ({
+export const dataExplorerToListParams = (dataExplorer: DataExplorer) => ({
     limit: dataExplorer.rowsPerPage,
     offset: dataExplorer.page * dataExplorer.rowsPerPage,
 });
index 814d5855c0b6a45b0ee0224d73e382f13b561560..00931bf8e3c95cd5d98404ac74a14b136253536a 100644 (file)
@@ -8,6 +8,8 @@ import { MiddlewareAPI } from "redux";
 import { DataColumns } from "~/components/data-table/data-table";
 import { dataExplorerActions } from "./data-explorer-action";
 import { SortDirection } from "~/components/data-table/data-column";
+import { createTree } from '~/models/tree';
+import { DataTableFilterItem } from "~/components/data-table-filters/data-table-filters-tree";
 
 
 describe("DataExplorerMiddleware", () => {
@@ -20,7 +22,7 @@ describe("DataExplorerMiddleware", () => {
                 selected: true,
                 configurable: false,
                 sortDirection: SortDirection.NONE,
-                filters: [],
+                filters: createTree<DataTableFilterItem>(),
                 render: jest.fn()
             }],
             requestItems: jest.fn(),
@@ -48,7 +50,7 @@ describe("DataExplorerMiddleware", () => {
                 selected: true,
                 configurable: false,
                 sortDirection: SortDirection.NONE,
-                filters: [],
+                filters: createTree<DataTableFilterItem>(),
                 render: jest.fn()
             }],
             requestItems: jest.fn(),
@@ -115,7 +117,7 @@ describe("DataExplorerMiddleware", () => {
         };
         const next = jest.fn();
         const middleware = dataExplorerMiddleware(service)(api)(next);
-        middleware(dataExplorerActions.SET_FILTERS({ id: service.getId(), columnName: "", filters: [] }));
+        middleware(dataExplorerActions.SET_FILTERS({ id: service.getId(), columnName: "", filters: createTree() }));
         expect(api.dispatch).toHaveBeenCalledTimes(2);
     });
 
index 1657ab70ed7bc1fbe13bc4c691f38d41084f45c2..613bf278edd81d67ad73cc84402ab991aa8353a7 100644 (file)
@@ -4,8 +4,8 @@
 
 import { DataColumn, toggleSortDirection, resetSortDirection, SortDirection } from "~/components/data-table/data-column";
 import { dataExplorerActions, DataExplorerAction } from "./data-explorer-action";
-import { DataTableFilterItem } from "~/components/data-table-filters/data-table-filters";
 import { DataColumns } from "~/components/data-table/data-table";
+import { DataTableFilters } from "~/components/data-table-filters/data-table-filters-tree";
 
 export interface DataExplorer {
     columns: DataColumns<any>;
@@ -103,7 +103,7 @@ const toggleColumn = (columnName: string) =>
         ? { ...column, selected: !column.selected }
         : column;
 
-const setFilters = (columnName: string, filters: DataTableFilterItem[]) =>
+const setFilters = (columnName: string, filters: DataTableFilters) =>
     (column: DataColumn<any>) => column.name === columnName
         ? { ...column, filters }
         : column;