Skip to content

Commit 603a6ad

Browse files
yordan-stgjulivan
authored andcommitted
feat: add configurable debounce interval for datasource filter
1 parent 5cfbee4 commit 603a6ad

File tree

13 files changed

+78
-16
lines changed

13 files changed

+78
-16
lines changed

packages/pluggableWidgets/combobox-web/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1515

1616
- We fixed an issue where combobox lazy load is not working on initial load.
1717

18+
### Added
19+
20+
- We added a configurable debounce interval for datasource filter operations via a new property in the Advanced tab.
21+
1822
## [2.5.0] - 2025-08-12
1923

2024
### Added

packages/pluggableWidgets/combobox-web/src/Combobox.editorConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export function getProperties(
152152
"selectionMethod",
153153
"selectAllButton",
154154
"selectAllButtonCaption",
155+
"datasourceFilterDebounceInterval",
155156
...ASSOCIATION_SOURCE_CONFIG,
156157
...DATABASE_SOURCE_CONFIG,
157158
...LAZY_LOADING_CONFIG

packages/pluggableWidgets/combobox-web/src/Combobox.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,10 @@
417417
<enumerationValue key="skeleton">Skeleton</enumerationValue>
418418
</enumerationValues>
419419
</property>
420+
<property key="datasourceFilterDebounceInterval" type="integer" required="true" defaultValue="200">
421+
<caption>Filter debounce</caption>
422+
<description>Debounce interval in milliseconds.</description>
423+
</property>
420424
</propertyGroup>
421425
<propertyGroup caption="Multiple-selection">
422426
<property key="selectedItemsSorting" type="enumeration" defaultValue="none" required="true">

packages/pluggableWidgets/combobox-web/src/__tests__/MultiSelection.spec.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ describe("Combo box (Association)", () => {
7777
selectedItemsSorting: "none",
7878
customEditability: "default",
7979
customEditabilityExpression: dynamic(false),
80-
filterInputDebounceInterval: 200
80+
filterInputDebounceInterval: 200,
81+
datasourceFilterDebounceInterval: 200
8182
};
8283
if (defaultProps.optionsSourceAssociationCaptionType === "expression") {
8384
defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic(`${i.id}`);

packages/pluggableWidgets/combobox-web/src/__tests__/SingleSelection.spec.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ describe("Combo box (Association)", () => {
8080
selectedItemsSorting: "none",
8181
customEditability: "default",
8282
customEditabilityExpression: dynamic(false),
83-
filterInputDebounceInterval: 200
83+
filterInputDebounceInterval: 200,
84+
datasourceFilterDebounceInterval: 200
8485
};
8586
if (defaultProps.optionsSourceAssociationCaptionType === "expression") {
8687
defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic(`${i.id}`);

packages/pluggableWidgets/combobox-web/src/__tests__/StaticSelection.spec.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ describe("Combo box (Static values)", () => {
7878
selectedItemsSorting: "none",
7979
customEditability: "default",
8080
customEditabilityExpression: dynamic(false),
81-
filterInputDebounceInterval: 200
81+
filterInputDebounceInterval: 200,
82+
datasourceFilterDebounceInterval: 200
8283
};
8384
if (defaultProps.optionsSourceAssociationCaptionType === "expression") {
8485
defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic(`${i.id}`);

packages/pluggableWidgets/combobox-web/src/helpers/Association/BaseAssociationSelector.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ export class BaseAssociationSelector<T extends string | string[], R extends Refe
4545
customContent,
4646
customContentType,
4747
lazyLoading,
48-
loadingType
48+
loadingType,
49+
datasourceFilterDebounceInterval
4950
] = extractAssociationProps(props);
5051

5152
this.lazyLoader.updateProps(ds);
@@ -64,7 +65,8 @@ export class BaseAssociationSelector<T extends string | string[], R extends Refe
6465
ds,
6566
filterType,
6667
lazyLoading,
67-
attributeId: captionType === "attribute" ? (captionProvider as ListAttributeValue<string>).id : undefined
68+
attributeId: captionType === "attribute" ? (captionProvider as ListAttributeValue<string>).id : undefined,
69+
datasourceFilterDebounceInterval
6870
});
6971

7072
if (

packages/pluggableWidgets/combobox-web/src/helpers/Association/utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ type ExtractionReturnValue = [
2828
ListWidgetValue | undefined,
2929
OptionsSourceAssociationCustomContentTypeEnum,
3030
boolean,
31-
LoadingTypeEnum
31+
LoadingTypeEnum,
32+
number
3233
];
3334

3435
export function extractAssociationProps(props: ComboboxContainerProps): ExtractionReturnValue {
3536
const attr = props.attributeAssociation;
3637
const filterType = props.filterType;
3738
const onChangeEvent = props.onChangeEvent;
39+
const datasourceFilterDebounceInterval = props.datasourceFilterDebounceInterval;
3840

3941
if (!attr) {
4042
throw new Error("'optionsSourceType' type is 'association' but 'attributeAssociation' is not defined.");
@@ -79,6 +81,7 @@ export function extractAssociationProps(props: ComboboxContainerProps): Extracti
7981
customContent,
8082
customContentType,
8183
lazyLoading,
82-
loadingType
84+
loadingType,
85+
datasourceFilterDebounceInterval
8386
];
8487
}

packages/pluggableWidgets/combobox-web/src/helpers/BaseDatasourceOptionsProvider.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { debounce } from "@mendix/widget-plugin-platform/utils/debounce";
12
import { ListAttributeValue, ListValue, ObjectItem } from "mendix";
23
import { FilterTypeEnum } from "../../typings/ComboboxProps";
34
import { BaseOptionsProvider } from "./BaseOptionsProvider";
@@ -10,12 +11,16 @@ export interface BaseProps {
1011
ds: ListValue;
1112
filterType: FilterTypeEnum;
1213
lazyLoading: boolean;
14+
datasourceFilterDebounceInterval?: number;
1315
}
1416

1517
export class BaseDatasourceOptionsProvider extends BaseOptionsProvider<ObjectItem, BaseProps> {
1618
private ds?: ListValue;
1719
private attributeId?: ListAttributeValue["id"];
1820
protected loading: boolean = false;
21+
private debouncedSetFilter?: (filterCondition: any) => void;
22+
private abortDebouncedFilter?: () => void;
23+
private datasourceFilterDebounceInterval: number = 200;
1924

2025
constructor(
2126
caption: CaptionsProvider,
@@ -24,6 +29,19 @@ export class BaseDatasourceOptionsProvider extends BaseOptionsProvider<ObjectIte
2429
super(caption);
2530
}
2631

32+
private createDebouncedSetFilter(): void {
33+
if (this.abortDebouncedFilter) {
34+
this.abortDebouncedFilter();
35+
}
36+
37+
const [debouncedFn, abort] = debounce((filterCondition: any) => {
38+
this.ds?.setFilter(filterCondition);
39+
}, this.datasourceFilterDebounceInterval);
40+
41+
this.debouncedSetFilter = debouncedFn;
42+
this.abortDebouncedFilter = abort;
43+
}
44+
2745
get sortOrder(): SortOrder {
2846
let sortDir: SortOrder = "asc";
2947
if (this.ds) {
@@ -50,11 +68,14 @@ export class BaseDatasourceOptionsProvider extends BaseOptionsProvider<ObjectIte
5068

5169
getAll(): string[] {
5270
if (this.lazyLoading && this.attributeId) {
71+
if (!this.debouncedSetFilter) {
72+
this.createDebouncedSetFilter();
73+
}
5374
if (this.searchTerm === "") {
54-
this.ds?.setFilter(undefined);
75+
this.debouncedSetFilter!(undefined);
5576
} else {
5677
const filterCondition = datasourceFilter(this.filterType, this.searchTerm, this.attributeId);
57-
this.ds?.setFilter(filterCondition);
78+
this.debouncedSetFilter!(filterCondition);
5879
}
5980

6081
return this.options;
@@ -88,8 +109,12 @@ export class BaseDatasourceOptionsProvider extends BaseOptionsProvider<ObjectIte
88109
// used for initial load of selected value in case options are lazy loaded
89110
loadSelectedValue(attributeValue: string, attrId?: ListAttributeValue["id"]): void {
90111
if (this.lazyLoading && this.ds && this.attributeId) {
112+
if (!this.debouncedSetFilter) {
113+
this.createDebouncedSetFilter();
114+
}
115+
91116
const filterCondition = datasourceFilter("containsExact", attributeValue, attrId ?? this.attributeId);
92-
this.ds?.setFilter(filterCondition);
117+
this.debouncedSetFilter!(filterCondition);
93118
this.ds.setLimit(1);
94119
}
95120
}
@@ -100,6 +125,12 @@ export class BaseDatasourceOptionsProvider extends BaseOptionsProvider<ObjectIte
100125
this.filterType = props.filterType;
101126
this.lazyLoading = props.lazyLoading;
102127

128+
const newInterval = props.datasourceFilterDebounceInterval ?? 200;
129+
if (newInterval !== this.datasourceFilterDebounceInterval) {
130+
this.datasourceFilterDebounceInterval = newInterval;
131+
this.createDebouncedSetFilter();
132+
}
133+
103134
if (this.lazyLoading) {
104135
if (props.ds.status === "loading") {
105136
this.loading = true;
@@ -113,4 +144,10 @@ export class BaseDatasourceOptionsProvider extends BaseOptionsProvider<ObjectIte
113144
items.forEach(i => this.valuesMap.set(i.id, i));
114145
this.options = Array.from(this.valuesMap.keys());
115146
}
147+
148+
cleanup(): void {
149+
if (this.abortDebouncedFilter) {
150+
this.abortDebouncedFilter();
151+
}
152+
}
116153
}

packages/pluggableWidgets/combobox-web/src/helpers/Database/DatabaseMultiSelectionSelector.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ export class DatabaseMultiSelectionSelector implements MultiSelector {
7575
filterType,
7676
lazyLoading,
7777
loadingType,
78-
valueSourceAttribute
78+
valueSourceAttribute,
79+
datasourceFilterDebounceInterval
7980
} = extractDatabaseProps(props);
8081

8182
if (
@@ -107,7 +108,8 @@ export class DatabaseMultiSelectionSelector implements MultiSelector {
107108
ds,
108109
filterType,
109110
lazyLoading,
110-
attributeId: captionType === "attribute" ? (captionProvider as ListAttributeValue<string>).id : undefined
111+
attributeId: captionType === "attribute" ? (captionProvider as ListAttributeValue<string>).id : undefined,
112+
datasourceFilterDebounceInterval
111113
});
112114

113115
if (this.selectionMethod === "rowclick" || this.customContentType === "yes") {

0 commit comments

Comments
 (0)