Skip to content

Commit 0039665

Browse files
Operators loaded using getOperators API
Updated the ColumnFilter examples to use state and updated minor styling for one of the example Fixed a minor issue related to state update Added some test coverage
1 parent e523b9a commit 0039665

4 files changed

Lines changed: 187 additions & 59 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { LocalDataSourceProvider } from "@vuu-ui/vuu-data-test";
2+
import { TextColumnFilterValueSetViaBtn } from "../../../../../showcase/src/examples/Filters/ColumnFilter.examples";
3+
4+
describe("ColumnFilter", () => {
5+
describe("WHEN text columnfilter is rendered", () => {
6+
beforeEach(() => {
7+
cy.mount(
8+
<LocalDataSourceProvider>
9+
<TextColumnFilterValueSetViaBtn />
10+
</LocalDataSourceProvider>,
11+
);
12+
});
13+
14+
it("THEN the component is rendered with an initial value", () => {
15+
const container = cy.findByTestId("columnfilter");
16+
container.should("have.class", "vuuColumnFilter");
17+
container.find("input").should("have.value", "AAOP.N");
18+
});
19+
20+
it("THEN the component shows suggestions on input", () => {
21+
const container = cy.findByTestId("columnfilter");
22+
container.find("input").clear().type("A");
23+
cy.findAllByRole("option", { name: "AAOO.L" }).should("be.visible");
24+
});
25+
26+
it("THEN component renders a new value provided via state set from outside the container", () => {
27+
const container = cy.findByTestId("columnfilter");
28+
const input = container.find("input");
29+
input.should("have.value", "AAOP.N");
30+
cy.contains("button", "AAOQ.OQ").realClick();
31+
input.should("have.value", "AAOQ.OQ");
32+
cy.contains("button", "AAOU.MI").realClick();
33+
input.should("have.value", "AAOU.MI");
34+
});
35+
});
36+
});

vuu-ui/packages/vuu-filters/src/column-filter/ColumnFilter.tsx

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ import columnFilterCss from "./ColumnFilter.css";
1515
import { getDataItemEditControl } from "@vuu-ui/vuu-data-react";
1616
import { ForwardedRef, forwardRef, ReactElement, useMemo } from "react";
1717
import { VuuTable } from "@vuu-ui/vuu-protocol-types";
18-
import { assertValidValue, Operator, useColumnFilter } from "./useColumnFilter";
18+
import { assertValidOperator, assertValidValue, Operator, useColumnFilter } from "./useColumnFilter";
1919
import { ColumnDescriptor } from "@vuu-ui/vuu-table-types";
20-
import { FilterOp } from "@vuu-ui/vuu-filter-types";
2120

2221
const classBase = "vuuColumnFilter";
2322
export type FilterValue = string | readonly string[] | number;
2423
export type ColumnFilterValue = FilterValue | [FilterValue, FilterValue];
2524

2625
export interface ColumnFilterProps extends SegmentedButtonGroupProps {
2726
column: ColumnDescriptor;
28-
operator?: FilterOp | "between";
27+
operator?: Operator;
28+
/**
29+
* Display operator picker.
30+
*/
2931
showOperatorPicker?: boolean;
3032
/**
3133
* VuuTable is required if typeahead support is expected.
@@ -68,19 +70,25 @@ export const ColumnFilter = forwardRef(function ColumnFilter(
6870

6971
const {
7072
op,
73+
allowedOperators,
7174
filterValue,
7275
inputProps,
7376
rangeInputProps,
7477
onOperatorChange,
7578
handleCommit,
7679
handleRangeCommit,
7780
} = useColumnFilter({
78-
column,
7981
operator,
82+
column,
8083
value,
8184
onFilterChange,
8285
});
8386

87+
useMemo(
88+
() => assertValidOperator(allowedOperators, column, operator),
89+
[column, operator, allowedOperators],
90+
);
91+
8492
useMemo(
8593
() => assertValidValue(column, op, filterValue),
8694
[column, op, filterValue],
@@ -106,23 +114,19 @@ export const ColumnFilter = forwardRef(function ColumnFilter(
106114
</Button>
107115
</MenuTrigger>
108116
<MenuPanel>
109-
<MenuItem onClick={() => onOperatorChange("=")}>=</MenuItem>
110-
<MenuItem onClick={() => onOperatorChange("!=")}>!=</MenuItem>
111-
<MenuItem onClick={() => onOperatorChange("starts")}>
112-
starts
113-
</MenuItem>
114-
<MenuItem onClick={() => onOperatorChange("ends")}>ends</MenuItem>
115-
<MenuItem onClick={() => onOperatorChange("contains")}>
116-
contains
117-
</MenuItem>
118-
<MenuItem onClick={() => onOperatorChange("between")}>
119-
between
120-
</MenuItem>
117+
{allowedOperators.map((operator) => (
118+
<MenuItem
119+
key={operator}
120+
onClick={() => onOperatorChange(operator)}
121+
>
122+
{operator}
123+
</MenuItem>
124+
))}
121125
</MenuPanel>
122126
</Menu>
123127
) : null}
124128
{getDataItemEditControl({
125-
InputProps: { inputProps },
129+
InputProps: { inputProps },
126130
dataDescriptor: column,
127131
onCommit: handleCommit,
128132
defaultValue: Array.isArray(filterValue) ? filterValue[0] : filterValue,

vuu-ui/packages/vuu-filters/src/column-filter/useColumnFilter.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FilterOp } from "@vuu-ui/vuu-filter-types";
1+
import { FilterClauseOp, FilterOp } from "@vuu-ui/vuu-filter-types";
22
import { ColumnDescriptor } from "@vuu-ui/vuu-table-types";
33
import {
44
ColumnFilterProps,
@@ -8,16 +8,28 @@ import {
88
import {
99
ChangeEventHandler,
1010
useCallback,
11-
useEffect,
1211
useMemo,
1312
useRef,
1413
useState,
1514
} from "react";
1615
import { CommitHandler, queryClosest } from "@vuu-ui/vuu-utils";
1716
import { VuuTypeaheadInputProps } from "@vuu-ui/vuu-ui-controls";
17+
import { getOperators } from "../filter-clause/operator-utils";
1818

1919
export type Operator = FilterOp | "between";
2020

21+
export const assertValidOperator = (
22+
allowedOperators: FilterClauseOp[],
23+
column: ColumnDescriptor,
24+
op: Operator,
25+
) => {
26+
if (!allowedOperators.find((filterClauseOp) => filterClauseOp === op)) {
27+
console.warn(
28+
`[useColumnFilter] '${op} not supported for column ${column.name}'`,
29+
);
30+
}
31+
};
32+
2133
export const assertValidValue = (
2234
{ serverDataType: _ }: ColumnDescriptor,
2335
operator: Operator,
@@ -56,11 +68,14 @@ export const useColumnFilter = ({
5668
}: ColumnFilterHookProps) => {
5769
const filterValue = useRef(value);
5870
const [op, setOp] = useState(operator);
71+
const allowedOperators = useMemo(() => getOperators(column), [column]);
5972

60-
useEffect(() => {
73+
useMemo(() => {
6174
if (value && value !== filterValue.current) {
6275
filterValue.current = value;
63-
onFilterChange?.(value, column.name, op);
76+
setTimeout(() => {
77+
onFilterChange?.(value, column.name, op);
78+
}, 100);
6479
}
6580
}, [value, column, op, onFilterChange]);
6681

@@ -168,6 +183,7 @@ export const useColumnFilter = ({
168183

169184
return {
170185
op,
186+
allowedOperators,
171187
filterValue: filterValue.current,
172188
inputProps,
173189
rangeInputProps,

0 commit comments

Comments
 (0)