Skip to content

Commit 100587c

Browse files
committed
feat: number filter grid wide filtering
1 parent 6c2e818 commit 100587c

File tree

10 files changed

+204
-43
lines changed

10 files changed

+204
-43
lines changed

packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.editorConfig.ts

+14-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
import {
2-
ContainerProps,
3-
ImageProps,
4-
structurePreviewPalette,
5-
StructurePreviewProps,
6-
text
7-
} from "@mendix/widget-plugin-platform/preview/structure-preview-api";
1+
import { hidePropertyIn, Properties } from "@mendix/pluggable-widgets-tools";
82
import {
93
emptyIcon,
104
emptyIconDark,
@@ -23,25 +17,26 @@ import {
2317
smallerThanIcon,
2418
smallerThanIconDark
2519
} from "@mendix/widget-plugin-filtering/preview/editor-preview-icons";
26-
import { hidePropertiesIn, hidePropertyIn, Properties } from "@mendix/pluggable-widgets-tools";
20+
import {
21+
ContainerProps,
22+
ImageProps,
23+
structurePreviewPalette,
24+
StructurePreviewProps,
25+
text
26+
} from "@mendix/widget-plugin-platform/preview/structure-preview-api";
2727

2828
import { DatagridNumberFilterPreviewProps, DefaultFilterEnum } from "../typings/DatagridNumberFilterProps";
2929

30-
export function getProperties(
31-
values: DatagridNumberFilterPreviewProps,
32-
defaultProperties: Properties,
33-
platform: "web" | "desktop"
34-
): Properties {
30+
export function getProperties(values: DatagridNumberFilterPreviewProps, defaultProperties: Properties): Properties {
3531
if (!values.adjustable) {
3632
hidePropertyIn(defaultProperties, values, "screenReaderButtonCaption");
3733
}
38-
if (platform === "web") {
39-
if (!values.advanced) {
40-
hidePropertiesIn(defaultProperties, values, ["onChange", "valueAttribute"]);
41-
}
42-
} else {
43-
hidePropertyIn(defaultProperties, values, "advanced");
34+
35+
if (values.attrChoice === "auto") {
36+
hidePropertyIn(defaultProperties, values, "attributes");
4437
}
38+
39+
hidePropertyIn(defaultProperties, {} as { linkedDs: unknown }, "linkedDs");
4540
return defaultProperties;
4641
}
4742

packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@ import { DatagridNumberFilterContainerProps } from "../typings/DatagridNumberFil
44
import { NumberFilterContainer } from "./components/NumberFilterContainer";
55
import { isLoadingDefaultValues } from "./utils/widget-utils";
66
import { withNumberFilterAPI } from "./hocs/withNumberFilterAPI";
7+
import { withLinkedAttributes } from "./hocs/withLinkedAttributes";
78

89
const container = withPreloader<DatagridNumberFilterContainerProps>(NumberFilterContainer, isLoadingDefaultValues);
9-
const Widget = withNumberFilterAPI(container);
10+
const FilterAuto = withNumberFilterAPI(container);
11+
const FilterLinked = withLinkedAttributes(container);
1012

1113
export default function DatagridNumberFilter(props: DatagridNumberFilterContainerProps): ReactElement {
12-
return <Widget {...props} />;
14+
const isAuto = props.attrChoice === "auto";
15+
16+
if (isAuto) {
17+
return <FilterAuto {...props} />;
18+
}
19+
20+
return <FilterLinked {...props} />;
1321
}

packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.xml

+28-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,35 @@
88
<properties>
99
<propertyGroup caption="General">
1010
<propertyGroup caption="General">
11-
<property key="advanced" type="boolean" defaultValue="false">
12-
<caption>Enable advanced options</caption>
11+
<property key="attrChoice" type="enumeration" defaultValue="auto">
12+
<caption>Filter attributes</caption>
1313
<description />
14+
<enumerationValues>
15+
<enumerationValue key="auto">Auto</enumerationValue>
16+
<enumerationValue key="linked">Manual</enumerationValue>
17+
</enumerationValues>
18+
</property>
19+
<property key="linkedDs" type="datasource" isLinked="true" isList="true">
20+
<caption>Datasource to Filter</caption>
21+
<description />
22+
</property>
23+
<property key="attributes" type="object" isList="true" required="false">
24+
<caption>Attributes</caption>
25+
<description>Select the attributes that the end-user may use for filtering.</description>
26+
<properties>
27+
<propertyGroup caption="General">
28+
<property key="attribute" type="attribute" dataSource="../linkedDs" isMetaData="true" required="true">
29+
<caption>Attribute</caption>
30+
<description />
31+
<attributeTypes>
32+
<attributeType name="AutoNumber" />
33+
<attributeType name="Decimal" />
34+
<attributeType name="Integer" />
35+
<attributeType name="Long" />
36+
</attributeTypes>
37+
</property>
38+
</propertyGroup>
39+
</properties>
1440
</property>
1541
<property key="defaultValue" type="expression" required="false">
1642
<caption>Default value</caption>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { createElement } from "react";
2+
import { AttributeMetaData } from "mendix";
3+
import { useFilterAPI } from "@mendix/widget-plugin-filtering/context";
4+
import { APIError } from "@mendix/widget-plugin-filtering/errors";
5+
import { error, value, Result } from "@mendix/widget-plugin-filtering/result-meta";
6+
import { Number_InputFilterInterface } from "@mendix/widget-plugin-filtering/typings/InputFilterInterface";
7+
import { Alert } from "@mendix/widget-plugin-component-kit/Alert";
8+
import { useConst } from "@mendix/widget-plugin-mobx-kit/react/useConst";
9+
import { useSetup } from "@mendix/widget-plugin-mobx-kit/react/useSetup";
10+
import { NumberStoreProvider } from "@mendix/widget-plugin-filtering/custom-filter-api/NumberStoreProvider";
11+
import { ISetupable } from "@mendix/widget-plugin-mobx-kit/setupable";
12+
import { Big } from "big.js";
13+
14+
interface RequiredProps {
15+
attributes: Array<{
16+
attribute: AttributeMetaData<Big>;
17+
}>;
18+
name: string;
19+
}
20+
21+
interface StoreProvider extends ISetupable {
22+
store: Number_InputFilterInterface;
23+
}
24+
25+
type Component<P extends object> = (props: P) => React.ReactElement;
26+
27+
export function withLinkedAttributes<P extends RequiredProps>(
28+
component: Component<P & InjectableFilterAPI>
29+
): Component<P> {
30+
const StoreInjector = withInjectedStore(component);
31+
32+
return function FilterAPIProvider(props) {
33+
const api = useStoreProvider(props);
34+
35+
if (api.hasError) {
36+
return <Alert bootstrapStyle="danger">{api.error.message}</Alert>;
37+
}
38+
39+
return <StoreInjector {...props} {...api.value} />;
40+
};
41+
}
42+
43+
function withInjectedStore<P extends object>(
44+
Component: Component<P & InjectableFilterAPI>
45+
): Component<P & { provider: StoreProvider; channel: string }> {
46+
return function StoreInjector(props) {
47+
const provider = useSetup(() => props.provider);
48+
return <Component {...props} filterStore={provider.store} parentChannelName={props.channel} />;
49+
};
50+
}
51+
52+
interface InjectableFilterAPI {
53+
filterStore: Number_InputFilterInterface;
54+
parentChannelName?: string;
55+
}
56+
57+
function useStoreProvider(props: RequiredProps): Result<{ provider: StoreProvider; channel: string }, APIError> {
58+
const filterAPI = useFilterAPI();
59+
return useConst(() => {
60+
if (filterAPI.hasError) {
61+
return error(filterAPI.error);
62+
}
63+
64+
return value({
65+
provider: new NumberStoreProvider(filterAPI.value, {
66+
attributes: props.attributes.map(obj => obj.attribute),
67+
dataKey: props.name
68+
}),
69+
channel: filterAPI.value.parentChannelName
70+
});
71+
});
72+
}

packages/pluggableWidgets/datagrid-number-filter-web/typings/DatagridNumberFilterProps.d.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,28 @@
44
* @author Mendix Widgets Framework Team
55
*/
66
import { CSSProperties } from "react";
7-
import { ActionValue, DynamicValue, EditableValue } from "mendix";
7+
import { ActionValue, AttributeMetaData, DynamicValue, EditableValue } from "mendix";
88
import { Big } from "big.js";
99

10+
export type AttrChoiceEnum = "auto" | "linked";
11+
12+
export interface AttributesType {
13+
attribute: AttributeMetaData<Big>;
14+
}
15+
1016
export type DefaultFilterEnum = "greater" | "greaterEqual" | "equal" | "notEqual" | "smaller" | "smallerEqual" | "empty" | "notEmpty";
1117

18+
export interface AttributesPreviewType {
19+
attribute: string;
20+
}
21+
1222
export interface DatagridNumberFilterContainerProps {
1323
name: string;
1424
class: string;
1525
style?: CSSProperties;
1626
tabIndex?: number;
17-
advanced: boolean;
27+
attrChoice: AttrChoiceEnum;
28+
attributes: AttributesType[];
1829
defaultValue?: DynamicValue<Big>;
1930
defaultFilter: DefaultFilterEnum;
2031
placeholder?: DynamicValue<string>;
@@ -37,7 +48,8 @@ export interface DatagridNumberFilterPreviewProps {
3748
readOnly: boolean;
3849
renderMode: "design" | "xray" | "structure";
3950
translate: (text: string) => string;
40-
advanced: boolean;
51+
attrChoice: AttrChoiceEnum;
52+
attributes: AttributesPreviewType[];
4153
defaultValue: string;
4254
defaultFilter: DefaultFilterEnum;
4355
placeholder: string;

packages/pluggableWidgets/datagrid-text-filter-web/typings/DatagridTextFilterProps.d.ts

-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ export interface DatagridTextFilterPreviewProps {
4747
readOnly: boolean;
4848
renderMode: "design" | "xray" | "structure";
4949
translate: (text: string) => string;
50-
advanced: boolean;
51-
customAttrs: CustomAttrsEnum;
5250
attrChoice: AttrChoiceEnum;
5351
attributes: AttributesPreviewType[];
5452
defaultValue: string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { disposeBatch } from "@mendix/widget-plugin-mobx-kit/disposeBatch";
2+
import { ISetupable } from "@mendix/widget-plugin-mobx-kit/setupable";
3+
import { FilterCondition } from "mendix/filters";
4+
import { isAnd, isTag } from "../condition-utils";
5+
import { FilterAPI } from "../context";
6+
import { NumberInputFilterStore } from "../stores/input/NumberInputFilterStore";
7+
import { Number_InputFilterInterface } from "../typings/InputFilterInterface";
8+
import { FilterSpec } from "./typings";
9+
10+
export class NumberStoreProvider implements ISetupable {
11+
private _store: NumberInputFilterStore;
12+
readonly dataKey: string;
13+
14+
constructor(private filterAPI: FilterAPI, spec: FilterSpec<Big>) {
15+
this.dataKey = spec.dataKey;
16+
this._store = new NumberInputFilterStore(
17+
spec.attributes,
18+
this.findInitFilter(filterAPI.sharedInitFilter, this.dataKey)
19+
);
20+
}
21+
22+
private findInitFilter(conditions: Array<FilterCondition | undefined>, key: string): FilterCondition | null {
23+
for (const cond of conditions) {
24+
if (cond && isAnd(cond)) {
25+
const [tag, initFilter] = cond.args;
26+
if (isTag(tag) && tag.arg1.value === key) {
27+
return initFilter;
28+
}
29+
}
30+
}
31+
32+
return null;
33+
}
34+
35+
get store(): Number_InputFilterInterface {
36+
return this._store;
37+
}
38+
39+
setup(): () => void {
40+
const [add, disposeAll] = disposeBatch();
41+
this.filterAPI.filterObserver.observe(this.dataKey, this._store);
42+
add(() => this.filterAPI.filterObserver.unobserve(this.dataKey));
43+
add(this.store.setup?.());
44+
45+
return disposeAll;
46+
}
47+
}

packages/shared/widget-plugin-filtering/src/stores/input/NumberInputFilterStore.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Big } from "big.js";
2-
import { ListAttributeValue } from "mendix";
2+
import { AttributeMetaData, ListAttributeValue, SimpleFormatter } from "mendix";
33
import { FilterCondition } from "mendix/filters";
44
import { action, comparer, makeObservable } from "mobx";
55
import { inputStateFromCond } from "../../condition-utils";
@@ -9,9 +9,11 @@ import { FilterData, InputData } from "../../typings/settings";
99
import { NumberArgument } from "./Argument";
1010
import { BaseInputFilterStore } from "./BaseInputFilterStore";
1111
import { baseNames } from "./fn-mappers";
12+
import { getFormatter } from "./store-utils";
1213

1314
type NumFns = FilterFunctionGeneric | FilterFunctionNonValue | FilterFunctionBinary;
1415
type Formatter = ListAttributeValue<Big>["formatter"];
16+
type AttrMeta = AttributeMetaData<Big> & { formatter?: SimpleFormatter<Big> };
1517

1618
export class NumberInputFilterStore
1719
extends BaseInputFilterStore<NumberArgument, NumFns>
@@ -20,9 +22,8 @@ export class NumberInputFilterStore
2022
readonly storeType = "input";
2123
readonly type = "number";
2224

23-
constructor(attributes: Array<ListAttributeValue<Big>>, initCond: FilterCondition | null) {
24-
let { formatter } = attributes[0];
25-
formatter = formatterFix(formatter);
25+
constructor(attributes: AttrMeta[], initCond: FilterCondition | null) {
26+
let formatter = formatterFix(getFormatter<Big>(attributes[0]));
2627
super(new NumberArgument(formatter), new NumberArgument(formatter), "equal", attributes);
2728
makeObservable(this, {
2829
updateProps: action,

packages/shared/widget-plugin-filtering/src/stores/input/StringInputFilterStore.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { FilterData, InputData } from "../../typings/settings";
1313
import { StringArgument } from "./Argument";
1414
import { BaseInputFilterStore } from "./BaseInputFilterStore";
1515
import { baseNames } from "./fn-mappers";
16+
import { getFormatter } from "./store-utils";
1617

1718
type StrFns = FilterFunctionString | FilterFunctionGeneric | FilterFunctionNonValue | FilterFunctionBinary;
1819
type AttrMeta = AttributeMetaData<string> & { formatter?: SimpleFormatter<string> };
@@ -25,7 +26,7 @@ export class StringInputFilterStore
2526
readonly type = "string";
2627

2728
constructor(attributes: AttrMeta[], initCond: FilterCondition | null) {
28-
const formatter = getFormatter(attributes[0]);
29+
const formatter = getFormatter<string>(attributes[0]);
2930
super(new StringArgument(formatter), new StringArgument(formatter), "equal", attributes);
3031
makeObservable(this, {
3132
updateProps: action,
@@ -93,12 +94,3 @@ export class StringInputFilterStore
9394
this.isInitialized = true;
9495
}
9596
}
96-
97-
function getFormatter(attr: AttrMeta): SimpleFormatter<string> {
98-
return (
99-
attr.formatter ?? {
100-
format: v => v ?? "",
101-
parse: v => ({ valid: true, value: v ?? "" })
102-
}
103-
);
104-
}

packages/shared/widget-plugin-filtering/src/stores/input/store-utils.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ListAttributeValue } from "mendix";
1+
import { ListAttributeValue, SimpleFormatter } from "mendix";
22
import { FilterCondition } from "mendix/filters";
33
import {
44
Date_InputFilterInterface,
@@ -52,3 +52,13 @@ export function isStringFilter(store: InputFilterInterface): store is String_Inp
5252
export function isDateFilter(store: InputFilterInterface): store is Date_InputFilterInterface {
5353
return store.arg1.type === "date";
5454
}
55+
56+
export function getFormatter<T>(attr: { formatter?: SimpleFormatter<T> }): SimpleFormatter<T> {
57+
return (
58+
attr.formatter ??
59+
({
60+
format: v => v ?? "",
61+
parse: v => ({ valid: true, value: v ?? "" })
62+
} as SimpleFormatter<T>)
63+
);
64+
}

0 commit comments

Comments
 (0)