Skip to content

Commit

Permalink
add filter support to datatable (#439)
Browse files Browse the repository at this point in the history
  • Loading branch information
heswell committed Jan 17, 2023
1 parent 102b0c7 commit ad3ddc9
Show file tree
Hide file tree
Showing 15 changed files with 196 additions and 48 deletions.
3 changes: 1 addition & 2 deletions vuu-ui/packages/vuu-data/src/data-source.ts
Expand Up @@ -75,8 +75,7 @@ export interface DataSourceColumnsMessage extends MessageWithClientViewportId {
}
export interface DataSourceFilterMessage extends MessageWithClientViewportId {
type: "filter";
filter: Filter;
filterQuery: string;
filter: DataSourceFilter;
}
export interface DataSourceGroupByMessage extends MessageWithClientViewportId {
type: "groupBy";
Expand Down
2 changes: 2 additions & 0 deletions vuu-ui/packages/vuu-datagrid-types/index.d.ts
@@ -1,3 +1,4 @@
import { Filter } from "@finos/vuu-filter-types";
import {
VuuAggType,
VuuColumnDataType,
Expand Down Expand Up @@ -56,6 +57,7 @@ export interface ColumnDescriptor {
export interface KeyedColumnDescriptor extends ColumnDescriptor {
align?: "left" | "right";
className?: string;
filter?: Filter;
flex?: number;
heading?: [...string[]];
isGroup?: boolean;
Expand Down
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-datatable/package.json
Expand Up @@ -19,6 +19,7 @@
"@salt-ds/core": "1.0.0",
"@salt-ds/icons": "1.0.0",
"@heswell/salt-lab": "1.0.0-alpha.0",
"@finos/vuu-filters": "0.0.26",
"@finos/vuu-utils": "0.0.26",
"classnames": "^2.2.6",
"react": "^17.0.2",
Expand Down
4 changes: 4 additions & 0 deletions vuu-ui/packages/vuu-datatable/src/TableHeaderCell.css
Expand Up @@ -25,6 +25,10 @@
padding: 0 0 0 3px;
}

.vuuTable-headerCell-inner:has(.vuuFilterIndicator){
padding-left: 0;
}

.vuuTable-headerCell-label {
align-items: center;
justify-content: var(--cell-align);
Expand Down
2 changes: 2 additions & 0 deletions vuu-ui/packages/vuu-datatable/src/TableHeaderCell.tsx
Expand Up @@ -8,6 +8,7 @@ import { TableColumnResizeHandler } from "./dataTableTypes";

import "./TableHeaderCell.css";
import { useContextMenu } from "@finos/vuu-popups";
import { FilterIndicator } from "./filter-indicator";

const classBase = "vuuTable-headerCell";

Expand Down Expand Up @@ -73,6 +74,7 @@ export const TableHeaderCell = ({
ref={rootRef}
>
<div className={`${classBase}-inner`}>
<FilterIndicator column={column} />
<div className={`${classBase}-label`}>{column.label}</div>
<SortIndicator sorted={column.sorted} />
{column.resizeable !== false ? (
Expand Down
15 changes: 15 additions & 0 deletions vuu-ui/packages/vuu-datatable/src/filter-indicator.css
@@ -0,0 +1,15 @@
.vuuFilterIndicator {
--menu-icon-size: 12px;
--menu-item-icon-color: black;
align-items: center;
cursor: pointer;
display: flex;
flex: 0 0 18px;
flex-direction: column;
justify-content: center;
position: relative;
}

.vuuFilterIndicator + .vuuTable-headerCell-inner {
padding-left: 0;
}
43 changes: 43 additions & 0 deletions vuu-ui/packages/vuu-datatable/src/filter-indicator.tsx
@@ -0,0 +1,43 @@
import { KeyedColumnDescriptor } from "@finos/vuu-datagrid-types";
import { Filter } from "@finos/vuu-filter-types";
import { useContextMenu } from "@finos/vuu-popups";
import cx from "classnames";
import { HTMLAttributes, useCallback } from "react";

import "./filter-indicator.css";

export const Direction = {
ASC: "asc",
DSC: "dsc",
};

export interface FilterIndicatorProps extends HTMLAttributes<HTMLDivElement> {
column: KeyedColumnDescriptor;
filter?: Filter;
}

export const FilterIndicator = ({ column, filter }: FilterIndicatorProps) => {
//TODO handle this at header level
const showContextMenu = useContextMenu();

const handleClick = useCallback(
(evt) => {
// if we do this through keyboard, need to get co-ords
evt.stopPropagation();
showContextMenu(evt, "filter", { column, filter });
},
[column, filter, showContextMenu]
);

if (!column.filter) {
return null;
}

return (
<div
className={cx("vuuFilterIndicator")}
data-icon="filter"
onClick={handleClick}
/>
);
};
5 changes: 5 additions & 0 deletions vuu-ui/packages/vuu-datatable/src/useDataTable.ts
Expand Up @@ -131,6 +131,11 @@ export const useDataTable = ({
type: "tableConfig",
groupBy: message.groupBy,
});
case "filter":
return dispatchColumnAction({
type: "tableConfig",
filter: message.filter,
});
case "sort":
return dispatchColumnAction({
type: "tableConfig",
Expand Down
38 changes: 37 additions & 1 deletion vuu-ui/packages/vuu-datatable/src/useTableModel.ts
Expand Up @@ -10,12 +10,16 @@ import {
metadataKeys,
} from "@finos/vuu-utils";

// TOSO eliminate this dependency
import { extractFilterForColumn } from "@finos/vuu-filters";

import { Reducer, useReducer } from "react";
import {
VuuColumnDataType,
VuuGroupBy,
VuuSort,
} from "@finos/vuu-protocol-types";
import { DataSourceFilter } from "@finos/vuu-data";

const DEFAULT_COLUMN_WIDTH = 100;
const KEY_OFFSET = metadataKeys.count;
Expand Down Expand Up @@ -87,6 +91,7 @@ export interface ColumnActionUpdateProp {

export interface ColumnActionTableConfig {
type: "tableConfig";
filter?: DataSourceFilter;
groupBy?: VuuGroupBy;
sort?: VuuSort;
}
Expand Down Expand Up @@ -263,9 +268,10 @@ function updateColumnProp(

function updateTableConfig(
state: GridModel,
{ groupBy, sort }: ColumnActionTableConfig
{ filter, groupBy, sort }: ColumnActionTableConfig
) {
const hasGroupBy = groupBy !== undefined;
const hasFilter = typeof filter?.filter === "string";
const hasSort = sort && sort.sortDefs.length > 0;

let result = state;
Expand All @@ -284,6 +290,13 @@ function updateTableConfig(
};
}

if (hasFilter) {
result = {
...state,
columns: applyFilterToColumns(state.columns, filter),
};
}

return result;
}

Expand All @@ -294,6 +307,7 @@ function replaceColumn(
return state.map((col) => (col.name === column.name ? column : col));
}

//TOSDO move these functions to utils
const applyGroupByToColumns = (
columns: KeyedColumnDescriptor[],
groupBy: VuuGroupBy
Expand Down Expand Up @@ -330,6 +344,28 @@ const applySortToColumns = (colunms: KeyedColumnDescriptor[], sort: VuuSort) =>
}
});

const applyFilterToColumns = (
columns: KeyedColumnDescriptor[],
{ filterStruct }: DataSourceFilter
) =>
columns.map((column) => {
// TODO this gives us a dependency on vuu-filters
const filter = extractFilterForColumn(filterStruct, column.name);
if (filter !== undefined) {
return {
...column,
filter,
};
} else if (column.filter) {
return {
...column,
filter: undefined,
};
} else {
return column;
}
});

const getSortType = (column: ColumnDescriptor, { sortDefs }: VuuSort) => {
const sortDef = sortDefs.find((sortCol) => sortCol.column === column.name);
if (sortDef) {
Expand Down
3 changes: 2 additions & 1 deletion vuu-ui/packages/vuu-filters/src/filter-input/FilterInput.tsx
Expand Up @@ -22,6 +22,7 @@ export const FilterInput = ({
existingFilter,
onSubmitFilter,
suggestionProvider,
...props
}: FilterInputProps) => {
const { editorRef, clearInput } = useCodeMirrorEditor({
existingFilter,
Expand All @@ -30,7 +31,7 @@ export const FilterInput = ({
});

return (
<div className={classBase}>
<div {...props} className={classBase}>
<Button className={`${classBase}-FilterButton`} data-icon="filter" />
<div className={`${classBase}-Editor`} ref={editorRef} />
<Button
Expand Down
4 changes: 3 additions & 1 deletion vuu-ui/packages/vuu-filters/src/filter-utils.ts
Expand Up @@ -311,7 +311,9 @@ const collectFiltersForColumn = (
results.push(ffc);
}
});
if (results.length === 1) {
if (results.length === 0) {
return undefined;
} else if (results.length === 1) {
return results[0];
}
return {
Expand Down
5 changes: 3 additions & 2 deletions vuu-ui/showcase/src/examples/Filters/useSuggestionProvider.ts
@@ -1,8 +1,9 @@
import { SchemaColumn, useTypeaheadSuggestions } from "@finos/vuu-data";
import { useTypeaheadSuggestions } from "@finos/vuu-data";
import { ISuggestionProvider, SuggestionType } from "@finos/vuu-filters";
import { TypeaheadParams, VuuTable } from "@finos/vuu-protocol-types";
import { useCallback, useRef } from "react";
import { Completion } from "@codemirror/autocomplete";
import { ColumnDescriptor } from "@finos/vuu-datagrid-types";

const tableColumns: Completion[] = [
{ label: "bbg" },
Expand Down Expand Up @@ -69,7 +70,7 @@ const getTypeaheadParams = (
};

export interface SuggestionProviderHookProps {
columns: SchemaColumn[];
columns: ColumnDescriptor[];
table: VuuTable;
}

Expand Down
41 changes: 38 additions & 3 deletions vuu-ui/showcase/src/examples/Table/Table.stories.tsx
Expand Up @@ -5,11 +5,14 @@ import { Column, DataTable } from "@finos/vuu-datatable";
import { Flexbox, View } from "@finos/vuu-layout";
import { Dialog } from "@finos/vuu-popups";
import { itemsChanged } from "@finos/vuu-utils";
import { FilterInput } from "@finos/vuu-filters";

import {
ToggleButton,
ToggleButtonGroup,
ToggleButtonGroupChangeEventHandler,
Toolbar,
Tooltray,
} from "@heswell/salt-lab";
import { Button } from "@salt-ds/core";
import {
Expand All @@ -22,6 +25,8 @@ import {
} from "react";
import { DragVisualizer } from "../../../../packages/vuu-datatable/src/DragVisualizer";
import { ErrorDisplay, useSchemas, useTestDataSource } from "../utils";
import { Filter } from "@finos/vuu-filter-types";
import { useSuggestionProvider } from "../Filters/useSuggestionProvider";

let displaySequence = 1;

Expand Down Expand Up @@ -163,9 +168,21 @@ export const VuuDataTable = () => {
tablename: tables[selectedIndex],
});

const table = useMemo(
() => ({ module: "SIMUL", table: tables[selectedIndex] }),
[selectedIndex, tables]
);

const configRef = useRef<GridConfig>(config);
const [tableConfig, setTableConfig] = useState<GridConfig>(config);

console.log({ columns });

const filterSuggestionProvider = useSuggestionProvider({
columns,
table,
});

useMemo(() => {
setTableConfig((configRef.current = config));
}, [config]);
Expand Down Expand Up @@ -216,6 +233,14 @@ export const VuuDataTable = () => {
dataSource.groupBy = ["currency", "exchange"];
}, [dataSource]);

const handleSubmitFilter = useCallback(
(filterStruct: Filter | undefined, filter: string, filterName?: string) => {
filterName && console.log(`named filter created '${filterName}'`);
dataSource.filter = { filter, filterStruct };
},
[dataSource]
);

if (error) {
return <ErrorDisplay>{error}</ErrorDisplay>;
}
Expand All @@ -237,8 +262,18 @@ export const VuuDataTable = () => {
} as CSSProperties
}
>
<Button onClick={groupByCurrency}>Currency</Button>
<Button onClick={groupByCurrencyExchange}>Currency, Exchange</Button>
<Tooltray>
<Button onClick={groupByCurrency}>Currency</Button>
<Button onClick={groupByCurrencyExchange}>Currency, Exchange</Button>
</Tooltray>
<Tooltray>
<FilterInput
existingFilter={dataSource.filter.filterStruct}
onSubmitFilter={handleSubmitFilter}
style={{ width: 300 }}
suggestionProvider={filterSuggestionProvider}
/>
</Tooltray>
</Toolbar>
<DataTable
allowConfigEditing
Expand All @@ -248,7 +283,7 @@ export const VuuDataTable = () => {
height={600}
onConfigChange={handleTableConfigChange}
onShowConfigEditor={showConfigEditor}
width={700}
width={750}
/>
<Dialog
className="vuuDialog-gridConfig"
Expand Down

0 comments on commit ad3ddc9

Please sign in to comment.