Skip to content

[WC-2870] filtering dropdown #1530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 95 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
6973a05
chore: add attributes to settings
iobuhov Feb 26, 2025
c1e3310
chore: change interface
iobuhov Feb 27, 2025
c2576fe
chore: rename controller
iobuhov Feb 28, 2025
d877a47
refactor: rename interface
iobuhov Feb 28, 2025
26c4bfc
feat: add filter observer
iobuhov Feb 28, 2025
a5cb06b
refactor: rename type
iobuhov Feb 28, 2025
3e6ee50
refactor: remove dead code
iobuhov Feb 28, 2025
02019c5
feat: add filter observer to filter api
iobuhov Feb 28, 2025
aa90bdc
chore: change personalization
iobuhov Mar 3, 2025
efa2bb0
chore: change attr type
iobuhov Mar 3, 2025
43bfaee
feat: add custom text filter
iobuhov Mar 3, 2025
bc6acab
chore: merge filters
iobuhov Mar 4, 2025
05b3719
feat: add state persistence
iobuhov Mar 4, 2025
eb6df31
chore: change tag exp to not equal
iobuhov Mar 4, 2025
bf771be
chore: add condition tagging
iobuhov Mar 4, 2025
5a1bb9d
chore: add filter initialization
iobuhov Mar 4, 2025
cb18c1b
chore: remove placeholder tag
iobuhov Mar 4, 2025
149de06
test: fix unit tests
iobuhov Mar 4, 2025
c533b94
test: fix unit tests
iobuhov Mar 4, 2025
6536f4f
chore: change label
iobuhov Mar 5, 2025
352bfc3
chore: fix types
iobuhov Mar 5, 2025
eb34d65
fix: update types
iobuhov Mar 7, 2025
23ea46b
fix: update types
iobuhov Mar 7, 2025
d324324
fix: update types
iobuhov Mar 7, 2025
08e64fd
fix: change types
iobuhov Mar 7, 2025
e7c143a
test: update settings schema
iobuhov Mar 7, 2025
09210fe
chore: update lockfile
iobuhov Apr 4, 2025
d5ab09a
feat: add custom text filter
iobuhov Mar 3, 2025
57de010
feat(datagrid-date-filter-web): add linked ds
iobuhov Apr 4, 2025
d7d47cf
feat: add date store provider
iobuhov Apr 4, 2025
df30228
feat: add store injector to date filter
iobuhov Apr 4, 2025
55eecd5
chore: show linked ds in settings
iobuhov Apr 9, 2025
ca3cd7c
feat: number filter grid wide filtering
gjulivan Mar 27, 2025
ea835dc
chore: update xml
iobuhov Apr 9, 2025
365bec5
chore(datagrid-number-filter-web): update editor config
iobuhov Apr 9, 2025
aafbd04
chore: update formatter logic
iobuhov Apr 9, 2025
2393cc3
chore: fix type error
iobuhov Apr 17, 2025
951a8d3
chore: resolve mobx warnings
iobuhov Apr 30, 2025
cddb03f
feat: add new set of properties
iobuhov Apr 11, 2025
93f5fc9
chore: split code into packages
iobuhov Apr 18, 2025
01431bb
chore: fix imports and remove old modules
iobuhov Apr 18, 2025
1b924e9
chore: move tests
iobuhov Apr 18, 2025
baca615
build: fix all type issues
iobuhov Apr 18, 2025
68cb2ef
refactor(!): remove legacy provider
iobuhov Apr 18, 2025
057a7c4
chore: move hook
iobuhov Apr 22, 2025
d6ddbc8
refactor: remove header filter
iobuhov Apr 22, 2025
55ad6fc
refactor: remove header filter
iobuhov Apr 23, 2025
1a40833
chore: introduce wide filter api
iobuhov Apr 23, 2025
f893f7c
chore: update imports
iobuhov Apr 23, 2025
bd8f33f
chore: remove dead code
iobuhov Apr 23, 2025
5b255a2
chore: start migrating dd filter
iobuhov Apr 23, 2025
a1b798f
refactor: switch to gate
iobuhov Apr 24, 2025
cd715f8
chore: update type
iobuhov Apr 24, 2025
bd86068
chore: migrate to gate
iobuhov Apr 24, 2025
e014e7a
refactor: move static filter container
iobuhov Apr 24, 2025
c99c7cc
chore: update ref filter props
iobuhov Apr 24, 2025
2056521
chore: create new component tree
iobuhov Apr 24, 2025
ea537ad
chore: finish enum filter
iobuhov Apr 25, 2025
5f832ca
feat: add linked ref store
iobuhov Apr 25, 2025
f31efb0
feat: switch to assc metadata
iobuhov Apr 25, 2025
0c3b63e
chore: restore code
iobuhov Apr 25, 2025
749672a
feat: first implementation of linked dd filter
iobuhov Apr 28, 2025
38647e0
chore: fix issue with ref
iobuhov Apr 28, 2025
4ef51b3
refactor: change to useSetup
iobuhov Apr 29, 2025
2f5ed47
fix: resolve linter issues
iobuhov Apr 29, 2025
716db5a
chore: move ds controllers to grid plugin
iobuhov Apr 30, 2025
07c00b5
chore: change DS controller
iobuhov Apr 30, 2025
b0a7e8b
chore: format file
iobuhov May 12, 2025
da48329
chore: add controller module
iobuhov May 12, 2025
b419b75
chore: fix build issues
iobuhov May 13, 2025
95e1a3e
chore: switch to new gallery store
iobuhov May 13, 2025
07f2006
chore: add root context
iobuhov May 13, 2025
ead47dc
chore: pass header as prop
iobuhov May 13, 2025
60fa963
feat: remove old gallery settings
iobuhov May 14, 2025
8736796
chore: remove outdated check
iobuhov May 14, 2025
bb862bc
chore: add observable sort store
iobuhov May 14, 2025
bf6aaf1
chore: change interfaces
iobuhov May 14, 2025
2bcb205
chore: change naming
iobuhov May 14, 2025
480fc73
chore: add sort store provider
iobuhov May 14, 2025
a7b3466
feat: add sort store injector
iobuhov May 15, 2025
2aa9bbc
chore: rename HOCs
iobuhov May 15, 2025
1b69e30
chore: updating unit test
iobuhov May 16, 2025
4899ea9
chore: redo the sort host
iobuhov May 16, 2025
c2c5a77
test: add tests for sort host
iobuhov May 16, 2025
405fb0a
feat: change sort host implementation
iobuhov May 19, 2025
5ed4882
chore: enable multisort
iobuhov May 20, 2025
cca23d1
chore: fix typings
iobuhov May 20, 2025
430c43e
fix: change header context
iobuhov May 20, 2025
74057a9
fix: add filter context
iobuhov May 20, 2025
8fbf72d
fix: catch invalid argument error
iobuhov May 20, 2025
6d776a0
chore: bump versions
iobuhov May 20, 2025
7b4b1c6
fix: remove typo
iobuhov May 20, 2025
d897fcc
chore: hide pagination props for now
iobuhov May 21, 2025
ca2afa6
chore: update lockfile
iobuhov May 21, 2025
83afdd4
chore: remove old xml
iobuhov May 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/modules/data-widgets/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@mendix/data-widgets",
"moduleName": "Data Widgets",
"version": "2.31.1",
"version": "3.0.0",
"copyright": "© Mendix Technology BV 2025. All rights reserved.",
"license": "Apache-2.0",
"private": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@mendix/datagrid-date-filter-web",
"widgetName": "DatagridDateFilter",
"version": "2.11.2",
"version": "3.0.0",
"description": "",
"copyright": "© Mendix Technology BV 2025. All rights reserved.",
"license": "Apache-2.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { hidePropertiesIn, hidePropertyIn, Properties } from "@mendix/pluggable-widgets-tools";
import {
betweenIcon,
betweenIconDark,
Expand All @@ -23,22 +24,23 @@ import {
import {
ContainerProps,
ImageProps,
structurePreviewPalette,
StructurePreviewProps,
text,
structurePreviewPalette
text
} from "@mendix/widget-plugin-platform/preview/structure-preview-api";
import { hidePropertiesIn, hidePropertyIn, Properties } from "@mendix/pluggable-widgets-tools";

import { DatagridDateFilterPreviewProps, DefaultFilterEnum } from "../typings/DatagridDateFilterProps";

export function getProperties(
values: DatagridDateFilterPreviewProps,
defaultProperties: Properties,
platform: "web" | "desktop"
): Properties {
export function getProperties(values: DatagridDateFilterPreviewProps, defaultProperties: Properties): Properties {
if (!values.adjustable) {
hidePropertyIn(defaultProperties, values, "screenReaderButtonCaption");
}

if (values.attrChoice === "auto") {
hidePropertyIn(defaultProperties, values, "attributes");
hidePropertyIn(defaultProperties, {} as { linkedDs: unknown }, "linkedDs");
}

if (values.defaultFilter !== "between") {
hidePropertiesIn(defaultProperties, values, [
"defaultStartDate",
Expand All @@ -49,13 +51,7 @@ export function getProperties(
} else {
hidePropertiesIn(defaultProperties, values, ["defaultValue", "valueAttribute"]);
}
if (platform === "web") {
if (!values.advanced) {
hidePropertiesIn(defaultProperties, values, ["onChange", "valueAttribute"]);
}
} else {
hidePropertyIn(defaultProperties, values, "advanced");
}

return defaultProperties;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ import { DatagridDateFilterContainerProps } from "../typings/DatagridDateFilterP
import { Container } from "./components/DateFilterContainer";
import { withDateFilterAPI } from "./hocs/withDateFilterAPI";
import { isLoadingDefaultValues } from "./utils/widget-utils";
import { withDateLinkedAttributes } from "./hocs/withDateLinkedAttributes";

const container = withPreloader(Container, isLoadingDefaultValues);
const Widget = withDateFilterAPI(container);
const FilterAuto = withDateFilterAPI(container);
const FilterLinked = withDateLinkedAttributes(container);

export default function DatagridDateFilter(props: DatagridDateFilterContainerProps): ReactElement | null {
return <Widget {...props} />;
const isAuto = props.attrChoice === "auto";

if (isAuto) {
return <FilterAuto {...props} />;
}

return <FilterLinked {...props} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,32 @@
<properties>
<propertyGroup caption="General">
<propertyGroup caption="General">
<property key="advanced" type="boolean" defaultValue="false">
<caption>Enable advanced options</caption>
<property key="attrChoice" type="enumeration" defaultValue="auto">
<caption>Filter attributes</caption>
<description />
<enumerationValues>
<enumerationValue key="auto">Auto</enumerationValue>
<enumerationValue key="linked">Custom</enumerationValue>
</enumerationValues>
</property>
<property key="linkedDs" type="datasource" isLinked="true" isList="true">
<caption>Datasource to Filter</caption>
<description />
</property>
<property key="attributes" type="object" isList="true" required="false">
<caption>Attributes</caption>
<description>Select the attributes that the end-user may use for filtering.</description>
<properties>
<propertyGroup caption="General">
<property key="attribute" type="attribute" dataSource="../linkedDs" isMetaData="true" required="true">
<caption>Attribute</caption>
<description />
<attributeTypes>
<attributeType name="DateTime" />
</attributeTypes>
</property>
</propertyGroup>
</properties>
</property>
<property key="defaultValue" type="expression" required="false">
<caption>Default value</caption>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "@testing-library/jest-dom";
import { FilterAPIv2 } from "@mendix/widget-plugin-filtering/context";
import { FilterAPI } from "@mendix/widget-plugin-filtering/context";
import {
HeaderFiltersStore,
HeaderFiltersStoreProps
HeaderFiltersStoreSpec
} from "@mendix/widget-plugin-filtering/stores/generic/HeaderFiltersStore";
import {
actionValue,
Expand All @@ -15,11 +15,7 @@ import { createContext, createElement } from "react";
import DatagridDateFilter from "../../DatagridDateFilter";
import { DatagridDateFilterContainerProps } from "../../../typings/DatagridDateFilterProps";
import { MXGlobalObject, MXSessionConfig } from "../../../typings/global";

interface StaticInfo {
name: string;
filtersChannelName: string;
}
import { FilterObserver } from "@mendix/widget-plugin-filtering/typings/FilterObserver";

function createMXObjectMock(
code: string,
Expand Down Expand Up @@ -54,13 +50,17 @@ const commonProps: DatagridDateFilterContainerProps = {
advanced: false
};

const headerFilterStoreInfo: StaticInfo = {
name: commonProps.name,
filtersChannelName: ""
};

const mxObject = createMXObjectMock("en_US", "en-US");

const mockSpec = (spec: Partial<HeaderFiltersStoreSpec>): HeaderFiltersStoreSpec => ({
filterList: [],
filterChannelName: "datagrid/1",
headerInitFilter: [],
sharedInitFilter: [],
customFilterHost: {} as FilterObserver,
...spec
});

describe("Date Filter", () => {
describe("with single instance", () => {
afterEach(() => {
Expand All @@ -69,13 +69,13 @@ describe("Date Filter", () => {

describe("with single attribute", () => {
beforeEach(() => {
const props: HeaderFiltersStoreProps = {
const spec = mockSpec({
filterList: [
{ filter: new ListAttributeValueBuilder().withType("DateTime").withFilterable(true).build() }
]
};
const headerFilterStore = new HeaderFiltersStore(props, headerFilterStoreInfo, null);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPIv2>(
});
const headerFilterStore = new HeaderFiltersStore(spec);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPI>(
headerFilterStore.context
);
(window as any).mx = mxObject;
Expand Down Expand Up @@ -144,7 +144,7 @@ describe("Date Filter", () => {

describe("with double attributes", () => {
beforeAll(() => {
const props: HeaderFiltersStoreProps = {
const spec = mockSpec({
filterList: [
{
filter: new ListAttributeValueBuilder()
Expand All @@ -161,9 +161,9 @@ describe("Date Filter", () => {
.build()
}
]
};
const headerFilterStore = new HeaderFiltersStore(props, headerFilterStoreInfo, null);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPIv2>(
});
const headerFilterStore = new HeaderFiltersStore(spec);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPI>(
headerFilterStore.context
);
(window as any).mx = mxObject;
Expand All @@ -184,13 +184,13 @@ describe("Date Filter", () => {

describe("with wrong attribute's type", () => {
beforeAll(() => {
const props: HeaderFiltersStoreProps = {
const spec = mockSpec({
filterList: [
{ filter: new ListAttributeValueBuilder().withType("Decimal").withFilterable(true).build() }
]
};
const headerFilterStore = new HeaderFiltersStore(props, headerFilterStoreInfo, null);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPIv2>(
});
const headerFilterStore = new HeaderFiltersStore(spec);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPI>(
headerFilterStore.context
);
(window as any).mx = mxObject;
Expand All @@ -211,7 +211,7 @@ describe("Date Filter", () => {

describe("with wrong multiple attributes' types", () => {
beforeAll(() => {
const props: HeaderFiltersStoreProps = {
const spec = mockSpec({
filterList: [
{
filter: new ListAttributeValueBuilder()
Expand All @@ -228,9 +228,9 @@ describe("Date Filter", () => {
.build()
}
]
};
const headerFilterStore = new HeaderFiltersStore(props, headerFilterStoreInfo, null);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPIv2>(
});
const headerFilterStore = new HeaderFiltersStore(spec);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPI>(
headerFilterStore.context
);
(window as any).mx = mxObject;
Expand Down Expand Up @@ -267,13 +267,13 @@ describe("Date Filter", () => {

describe("with multiple instances", () => {
beforeAll(() => {
const props: HeaderFiltersStoreProps = {
const spec = mockSpec({
filterList: [
{ filter: new ListAttributeValueBuilder().withType("DateTime").withFilterable(true).build() }
]
};
const headerFilterStore = new HeaderFiltersStore(props, headerFilterStoreInfo, null);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPIv2>(
});
const headerFilterStore = new HeaderFiltersStore(spec);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPI>(
headerFilterStore.context
);
(window as any).mx = mxObject;
Expand All @@ -296,13 +296,13 @@ describe("Date Filter", () => {

describe("with session config", () => {
beforeEach(() => {
const props: HeaderFiltersStoreProps = {
const spec = mockSpec({
filterList: [
{ filter: new ListAttributeValueBuilder().withType("DateTime").withFilterable(true).build() }
]
};
const headerFilterStore = new HeaderFiltersStore(props, headerFilterStoreInfo, null);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPIv2>(
});
const headerFilterStore = new HeaderFiltersStore(spec);
(window as any)["com.mendix.widgets.web.filterable.filterContext.v2"] = createContext<FilterAPI>(
headerFilterStore.context
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function withDateFilterAPI<P extends object>(
Component: (props: P & Date_FilterAPIv2) => React.ReactElement
): (props: P) => React.ReactElement {
return function FilterAPIProvider(props) {
const api = useDateFilterAPI("");
const api = useDateFilterAPI();

if (api.hasError) {
return <Alert bootstrapStyle="danger">{api.error.message}</Alert>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { createElement } from "react";
import { AttributeMetaData } from "mendix";
import { useFilterAPI } from "@mendix/widget-plugin-filtering/context";
import { APIError } from "@mendix/widget-plugin-filtering/errors";
import { error, value, Result } from "@mendix/widget-plugin-filtering/result-meta";
import { Date_InputFilterInterface } from "@mendix/widget-plugin-filtering/typings/InputFilterInterface";
import { Alert } from "@mendix/widget-plugin-component-kit/Alert";
import { useConst } from "@mendix/widget-plugin-mobx-kit/react/useConst";
import { useSetup } from "@mendix/widget-plugin-mobx-kit/react/useSetup";
import { DateStoreProvider } from "@mendix/widget-plugin-filtering/custom-filter-api/DateStoreProvider";
import { ISetupable } from "@mendix/widget-plugin-mobx-kit/setupable";

interface RequiredProps {
attributes: Array<{
attribute: AttributeMetaData<Date>;
}>;
name: string;
}

interface StoreProvider extends ISetupable {
store: Date_InputFilterInterface;
}

type Component<P extends object> = (props: P) => React.ReactElement;

export function withDateLinkedAttributes<P extends RequiredProps>(
component: Component<P & InjectableFilterAPI>
): Component<P> {
const StoreInjector = withInjectedStore(component);

return function FilterAPIProvider(props) {
const api = useStoreProvider(props);

if (api.hasError) {
return <Alert bootstrapStyle="danger">{api.error.message}</Alert>;
}

return <StoreInjector {...props} {...api.value} />;
};
}

function withInjectedStore<P extends object>(
Component: Component<P & InjectableFilterAPI>
): Component<P & { provider: StoreProvider; channel: string }> {
return function StoreInjector(props) {
const provider = useSetup(() => props.provider);
return <Component {...props} filterStore={provider.store} parentChannelName={props.channel} />;
};
}

interface InjectableFilterAPI {
filterStore: Date_InputFilterInterface;
parentChannelName?: string;
}

function useStoreProvider(props: RequiredProps): Result<{ provider: StoreProvider; channel: string }, APIError> {
const filterAPI = useFilterAPI();
return useConst(() => {
if (filterAPI.hasError) {
return error(filterAPI.error);
}

return value({
provider: new DateStoreProvider(filterAPI.value, {
attributes: props.attributes.map(obj => obj.attribute),
dataKey: props.name
}),
channel: filterAPI.value.parentChannelName
});
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<package xmlns="http://www.mendix.com/package/1.0/">
<clientModule name="DatagridDateFilter" version="2.11.2" xmlns="http://www.mendix.com/clientModule/1.0/">
<clientModule name="DatagridDateFilter" version="3.0.0" xmlns="http://www.mendix.com/clientModule/1.0/">
<widgetFiles>
<widgetFile path="DatagridDateFilter.xml" />
</widgetFiles>
Expand Down
Loading