diff --git a/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss b/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss
index e311d2e0d9..9159447bd4 100644
--- a/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss
+++ b/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss
@@ -429,6 +429,86 @@ $root: ".widget-datagrid";
display: contents;
}
+ &-refresh-container {
+ grid-column: 1 / -1;
+ padding: 0;
+ position: relative;
+ }
+
+ &-refresh-indicator {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background-color: var(--border-color-default, #ced0d3);
+ border: none;
+ border-radius: 2px;
+ color: var(--brand-primary, $dg-brand-primary);
+ height: 4px;
+ width: 100%;
+ position: absolute;
+ left: 0;
+ right: 0;
+
+ &::-webkit-progress-bar {
+ background-color: transparent;
+ }
+
+ &::-webkit-progress-value {
+ background-color: currentColor;
+ transition: all 0.2s;
+ }
+
+ &::-moz-progress-bar {
+ background-color: currentColor;
+ transition: all 0.2s;
+ }
+
+ &::-ms-fill {
+ border: none;
+ background-color: currentColor;
+ transition: all 0.2s;
+ }
+
+ &:indeterminate {
+ background-size: 200% 100%;
+ background-image: linear-gradient(
+ to right,
+ transparent 50%,
+ currentColor 50%,
+ currentColor 60%,
+ transparent 60%,
+ transparent 71.5%,
+ currentColor 71.5%,
+ currentColor 84%,
+ transparent 84%
+ );
+ animation: progress-linear 3s infinite linear;
+ }
+
+ &:indeterminate::-moz-progress-bar {
+ background-color: transparent;
+ }
+
+ &:indeterminate::-ms-fill {
+ animation-name: none;
+ }
+
+ @keyframes progress-linear {
+ 0% {
+ background-size: 200% 100%;
+ background-position: left -31.25% top 0%;
+ }
+ 50% {
+ background-size: 800% 100%;
+ background-position: left -49% top 0%;
+ }
+ 100% {
+ background-size: 400% 100%;
+ background-position: left -102% top 0%;
+ }
+ }
+ }
+
&.widget-datagrid-selection-method-click {
.tr.tr-selected .td {
background-color: $dg-grid-selected-row-background;
diff --git a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md
index 59cfbca641..3d9e9a07d9 100644
--- a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md
+++ b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]
+### Added
+
+- We implemented a new property to show a refresh indicator. With the refresh indicator, any datasource change shows a progress bar on top of Datagrid 2.
+
## [3.0.1] - 2025-08-05
### Fixed
diff --git a/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorPreview.tsx b/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorPreview.tsx
index ba1df3f098..26839e163d 100644
--- a/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorPreview.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorPreview.tsx
@@ -141,10 +141,12 @@ export function preview(props: DatagridPreviewProps): ReactElement {
cellEventsController={eventsController}
checkboxEventsController={eventsController}
focusController={focusController}
+ isFirstLoad={false}
isLoading={false}
isFetchingNextBatch={false}
loadingType="spinner"
columnsLoading={false}
+ showRefreshIndicator={false}
/>
);
}
diff --git a/packages/pluggableWidgets/datagrid-web/src/Datagrid.tsx b/packages/pluggableWidgets/datagrid-web/src/Datagrid.tsx
index 78abad3963..d62e598cfe 100644
--- a/packages/pluggableWidgets/datagrid-web/src/Datagrid.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/Datagrid.tsx
@@ -1,22 +1,22 @@
+import { useOnResetFiltersEvent } from "@mendix/widget-plugin-external-events/hooks";
+import { useClickActionHelper } from "@mendix/widget-plugin-grid/helpers/ClickActionHelper";
+import { useFocusTargetController } from "@mendix/widget-plugin-grid/keyboard-navigation/useFocusTargetController";
import { useSelectionHelper } from "@mendix/widget-plugin-grid/selection";
import { generateUUID } from "@mendix/widget-plugin-platform/framework/generate-uuid";
+import { observer } from "mobx-react-lite";
import { ReactElement, ReactNode, createElement, useCallback, useMemo } from "react";
import { DatagridContainerProps } from "../typings/DatagridProps";
import { Cell } from "./components/Cell";
import { Widget } from "./components/Widget";
import { WidgetHeaderContext } from "./components/WidgetHeaderContext";
-import { useSelectActionHelper } from "./helpers/SelectActionHelper";
-import { useClickActionHelper } from "@mendix/widget-plugin-grid/helpers/ClickActionHelper";
+import { ProgressStore } from "./features/data-export/ProgressStore";
+import { useDataExport } from "./features/data-export/useDataExport";
import { useCellEventsController } from "./features/row-interaction/CellEventsController";
import { useCheckboxEventsController } from "./features/row-interaction/CheckboxEventsController";
-import { useFocusTargetController } from "@mendix/widget-plugin-grid/keyboard-navigation/useFocusTargetController";
-import { useOnResetFiltersEvent } from "@mendix/widget-plugin-external-events/hooks";
+import { useSelectActionHelper } from "./helpers/SelectActionHelper";
import { IColumnGroupStore } from "./helpers/state/ColumnGroupStore";
-import { observer } from "mobx-react-lite";
import { RootGridStore } from "./helpers/state/RootGridStore";
import { useRootStore } from "./helpers/state/useRootStore";
-import { useDataExport } from "./features/data-export/useDataExport";
-import { ProgressStore } from "./features/data-export/ProgressStore";
interface Props extends DatagridContainerProps {
columnsStore: IColumnGroupStore;
@@ -118,10 +118,12 @@ const Container = observer((props: Props): ReactElement => {
cellEventsController={cellEventsController}
checkboxEventsController={checkboxEventsController}
focusController={focusController}
- isLoading={rootStore.loaderCtrl.isLoading}
+ isFirstLoad={rootStore.loaderCtrl.isFirstLoad}
isFetchingNextBatch={rootStore.loaderCtrl.isFetchingNextBatch}
+ isLoading={props.datasource.status === "loading"}
loadingType={props.loadingType}
columnsLoading={!columnsStore.loaded}
+ showRefreshIndicator={rootStore.loaderCtrl.showRefreshIndicator}
/>
);
});
diff --git a/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml b/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml
index 8782abf1a6..98b1ccd98f 100644
--- a/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml
+++ b/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml
@@ -57,6 +57,10 @@
Skeleton
+
+ Show refresh indicator
+ Show a refresh indicator when the data is being loaded.
+
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx b/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx
index dc3bac60c7..64c1ca93b8 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx
@@ -8,7 +8,7 @@ interface Props {
className?: string;
children?: React.ReactNode;
loadingType: LoadingTypeEnum;
- isLoading: boolean;
+ isFirstLoad: boolean;
isFetchingNextBatch?: boolean;
columnsHidable: boolean;
columnsSize: number;
@@ -20,7 +20,7 @@ export function GridBody(props: Props): ReactElement {
const { children } = props;
const content = (): React.ReactElement => {
- if (props.isLoading) {
+ if (props.isFirstLoad) {
return 0 ? props.rowsSize : props.pageSize} />;
}
return (
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/RefreshIndicator.tsx b/packages/pluggableWidgets/datagrid-web/src/components/RefreshIndicator.tsx
new file mode 100644
index 0000000000..79f3f3a10f
--- /dev/null
+++ b/packages/pluggableWidgets/datagrid-web/src/components/RefreshIndicator.tsx
@@ -0,0 +1,11 @@
+import { createElement, ReactElement } from "react";
+
+export function RefreshIndicator(): ReactElement {
+ return (
+
+ );
+}
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx b/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx
index c3d1beeba2..f169d66fa8 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx
@@ -25,6 +25,7 @@ import { FocusTargetController } from "@mendix/widget-plugin-grid/keyboard-navig
import { observer } from "mobx-react-lite";
import { RowsRenderer } from "./RowsRenderer";
import { GridHeader } from "./GridHeader";
+import { RefreshIndicator } from "./RefreshIndicator";
export interface WidgetProps {
CellComponent: CellComponent;
@@ -65,10 +66,12 @@ export interface WidgetProps(props: WidgetProps): ReactElemen
paging,
pagingPosition,
preview,
+ showRefreshIndicator,
selectActionHelper,
setPage,
visibleColumns
@@ -189,8 +193,9 @@ const Main = observer((props: WidgetProps): ReactElemen
isLoading={props.columnsLoading}
preview={props.preview}
/>
+ {showRefreshIndicator ? : null}
1,
+ refreshIndicator: props.refreshIndicator,
query
});
}
diff --git a/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx b/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx
index 9a347d20aa..3eb9a22f31 100644
--- a/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx
@@ -105,10 +105,12 @@ export function mockWidgetProps(): WidgetProps {
selectActionHelper: mockSelectionProps(),
cellEventsController: { getProps: () => Object.create({}) },
checkboxEventsController: { getProps: () => Object.create({}) },
+ isFirstLoad: false,
isLoading: false,
isFetchingNextBatch: false,
loadingType: "spinner",
columnsLoading: false,
+ showRefreshIndicator: false,
focusController: new FocusTargetController(
new PositionController(),
new VirtualGridLayout(1, columns.length, 10)
diff --git a/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts b/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts
index edbce23cfd..052bb22244 100644
--- a/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts
+++ b/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts
@@ -96,6 +96,7 @@ export interface DatagridContainerProps {
itemSelectionMode: ItemSelectionModeEnum;
showSelectAllToggle: boolean;
loadingType: LoadingTypeEnum;
+ refreshIndicator: boolean;
columns: ColumnsType[];
columnsFilterable: boolean;
pageSize: number;
@@ -144,6 +145,7 @@ export interface DatagridPreviewProps {
itemSelectionMode: ItemSelectionModeEnum;
showSelectAllToggle: boolean;
loadingType: LoadingTypeEnum;
+ refreshIndicator: boolean;
columns: ColumnsPreviewType[];
columnsFilterable: boolean;
pageSize: number | null;
diff --git a/packages/shared/widget-plugin-grid/src/__tests__/DatasourceController.spec.ts b/packages/shared/widget-plugin-grid/src/__tests__/DatasourceController.spec.ts
index c2cbe59476..4385be3258 100644
--- a/packages/shared/widget-plugin-grid/src/__tests__/DatasourceController.spec.ts
+++ b/packages/shared/widget-plugin-grid/src/__tests__/DatasourceController.spec.ts
@@ -1,74 +1,134 @@
+import { BaseControllerHost } from "@mendix/widget-plugin-mobx-kit/BaseControllerHost";
import { GateProvider } from "@mendix/widget-plugin-mobx-kit/GateProvider";
-import { ReactiveControllerHost } from "@mendix/widget-plugin-mobx-kit/main";
-import { list } from "@mendix/widget-plugin-test-utils";
+// import { ReactiveControllerHost } from "@mendix/widget-plugin-mobx-kit/main";
+import { list, obj } from "@mendix/widget-plugin-test-utils";
import { ListValue } from "mendix";
import { DatasourceController } from "../query/DatasourceController";
+class TestControllerHost extends BaseControllerHost {}
+
describe("DatasourceController loading states", () => {
let controller: DatasourceController;
let datasource: ListValue;
- let provider: GateProvider<{ datasource: ListValue }>;
+ let provider: GateProvider<{ datasource: ListValue; refreshIndicator: boolean; refreshInterval: number }>;
beforeEach(() => {
- const host = { addController: jest.fn() } as unknown as ReactiveControllerHost;
- provider = new GateProvider({ datasource: list.loading() });
+ const host = new TestControllerHost();
+ provider = new GateProvider({ datasource: list.loading(), refreshIndicator: false, refreshInterval: 0 });
+ host.setup();
controller = new DatasourceController(host, { gate: provider.gate });
+ controller.setup();
});
describe("when datasource is loading", () => {
beforeEach(() => {
datasource = list.loading();
- provider.setProps({ datasource });
+ provider.setProps({ datasource, refreshIndicator: false, refreshInterval: 0 });
+ });
+
+ it("isFirstLoad returns true when loading and items undefined", () => {
+ expect(controller.isFirstLoad).toBe(true);
});
- it("isLoading returns true by default", () => {
- expect(controller.isLoading).toBe(true);
+ it("backgroundRefresh does not trigger reload if already loading", () => {
+ const reloadSpy = jest.spyOn(provider.gate.props.datasource, "reload");
+ controller.backgroundRefresh();
+ expect(reloadSpy).not.toHaveBeenCalled();
});
- it("refresh has no effect if ds is loading", () => {
- expect(provider.gate.props.datasource.status).toBe("loading");
- controller.refresh();
+ it("isRefreshing is false when loading and items undefined", () => {
expect(controller.isRefreshing).toBe(false);
});
- it("isRefreshing is true after refresh call", () => {
- provider.setProps({ datasource: list(0) });
- expect(provider.gate.props.datasource.status).toBe("available");
- controller.refresh();
+ it("isRefreshing is true after backgroundRefresh when items are present", () => {
+ // Set datasource to available with items
+ provider.setProps({ datasource: list(1), refreshIndicator: false, refreshInterval: 0 });
+ // Simulate refresh
+ controller.backgroundRefresh();
+ // Replace datasource with a mock in loading state and items present
+ const loadingWithItems = { ...list.loading(), items: [obj()] };
+ provider.setProps({ datasource: loadingWithItems, refreshIndicator: false, refreshInterval: 0 });
expect(controller.isRefreshing).toBe(true);
- provider.setProps({ datasource: list.loading() });
- expect(provider.gate.props.datasource.status).toBe("loading");
- expect(controller.isRefreshing).toBe(true);
- expect(controller.isLoading).toBe(false);
});
- it("isFetchingNextBatch returns true after setLimit call", () => {
+ it("isFetchingNextBatch is true after setLimit call", () => {
controller.setLimit(20);
expect(controller.isFetchingNextBatch).toBe(true);
- expect(controller.isLoading).toBe(false);
+ });
+
+ it("isSilentRefresh is true after backgroundRefresh", () => {
+ provider.setProps({ datasource: list(1), refreshIndicator: false, refreshInterval: 0 });
+ controller.backgroundRefresh();
+ expect(controller.isSilentRefresh).toBe(true);
});
});
describe("when datasource is not loading", () => {
beforeEach(() => {
datasource = list(0);
- provider.setProps({ datasource });
+ provider.setProps({ datasource, refreshIndicator: false, refreshInterval: 0 });
});
- it("all loading states return false", () => {
- expect(controller.isLoading).toBe(false);
+ it("All loading states return false", () => {
+ expect(controller.isFirstLoad).toBe(false);
expect(controller.isRefreshing).toBe(false);
expect(controller.isFetchingNextBatch).toBe(false);
+ expect(controller.isSilentRefresh).toBe(false);
});
- it("triggers refresh when called", () => {
- controller.refresh();
- expect(datasource.reload).toHaveBeenCalled();
+ it("backgroundRefresh triggers reload when not loading", () => {
+ const reloadSpy = jest.spyOn(datasource, "reload");
+ controller.backgroundRefresh();
+ expect(reloadSpy).toHaveBeenCalled();
});
- it("triggers setLimit when called", () => {
+ it("setLimit triggers setLimit on datasource", () => {
+ const setLimitSpy = jest.spyOn(datasource, "setLimit");
controller.setLimit(20);
- expect(datasource.setLimit).toHaveBeenCalledWith(20);
+ expect(setLimitSpy).toHaveBeenCalledWith(20);
+ });
+
+ it("setOffset triggers setOffset and resets flags", () => {
+ const setOffsetSpy = jest.spyOn(datasource, "setOffset");
+ controller.setOffset(5);
+ expect(setOffsetSpy).toHaveBeenCalledWith(5);
+ });
+
+ it("setSortOrder triggers setSortOrder and resets flags", () => {
+ const setSortOrderSpy = jest.spyOn(datasource, "setSortOrder");
+ // Use Mendix Option_2 structure (runtime shape)
+ const sortOption = { some: [{ attribute: "name", direction: "asc" }] };
+ // @ts-expect-error: Mendix Option_2 type not available in workspace
+ controller.setSortOrder(sortOption);
+ expect(setSortOrderSpy).toHaveBeenCalledWith(sortOption);
+ });
+
+ it("setFilter triggers setFilter and resets flags", () => {
+ const setFilterSpy = jest.spyOn(datasource, "setFilter");
+ // Use Mendix Option_2 structure (runtime shape)
+ const filterOption = { some: { attribute: "name", operator: "equals", value: "test" } };
+ // @ts-expect-error: Mendix Option_2 type not available in workspace
+ controller.setFilter(filterOption);
+ expect(setFilterSpy).toHaveBeenCalledWith(filterOption);
+ });
+
+ it("setPageSize updates pageSize property", () => {
+ controller.setPageSize(50);
+ // @ts-expect-error: private property
+ expect(controller.pageSize).toBe(50);
+ });
+
+ it("requestTotalCount triggers datasource.requestTotalCount", () => {
+ const spy = jest.spyOn(datasource, "requestTotalCount");
+ controller.requestTotalCount(true);
+ expect(spy).toHaveBeenCalledWith(true);
+ });
+
+ it("derivedQuery returns a computed value and updates on datasource change", () => {
+ const derived = controller.derivedQuery;
+ expect(typeof derived.get).toBe("function");
+ provider.setProps({ datasource: list(2), refreshIndicator: false, refreshInterval: 0 });
+ expect(derived.get()).toBeInstanceOf(DatasourceController);
});
});
});
diff --git a/packages/shared/widget-plugin-grid/src/__tests__/RefreshController.spec.ts b/packages/shared/widget-plugin-grid/src/__tests__/RefreshController.spec.ts
index 63833d4c97..582b35e06f 100644
--- a/packages/shared/widget-plugin-grid/src/__tests__/RefreshController.spec.ts
+++ b/packages/shared/widget-plugin-grid/src/__tests__/RefreshController.spec.ts
@@ -3,8 +3,8 @@ import { RefreshController } from "../query/RefreshController";
describe("RefreshController", () => {
let host: ReactiveControllerHost;
- let queryHelper: { refresh: jest.Mock };
- let atom: { get: () => { refresh: jest.Mock } };
+ let queryHelper: { backgroundRefresh: jest.Mock };
+ let atom: { get: () => { backgroundRefresh: jest.Mock } };
let addControllerMock: jest.Mock;
beforeEach(() => {
@@ -13,7 +13,7 @@ describe("RefreshController", () => {
addController: addControllerMock
} as unknown as ReactiveControllerHost;
queryHelper = {
- refresh: jest.fn()
+ backgroundRefresh: jest.fn()
};
atom = { get: () => queryHelper };
jest.useFakeTimers();
@@ -40,7 +40,7 @@ describe("RefreshController", () => {
expect(dispose).toBeInstanceOf(Function);
jest.advanceTimersByTime(1000);
- expect(queryHelper.refresh).toHaveBeenCalledTimes(1);
+ expect(queryHelper.backgroundRefresh).toHaveBeenCalledTimes(1);
});
it("should clear the timer when dispose is called", () => {
@@ -50,6 +50,6 @@ describe("RefreshController", () => {
dispose!();
jest.advanceTimersByTime(1000);
- expect(queryHelper.refresh).not.toHaveBeenCalled();
+ expect(queryHelper.backgroundRefresh).not.toHaveBeenCalled();
});
});
diff --git a/packages/shared/widget-plugin-grid/src/query/DatasourceController.ts b/packages/shared/widget-plugin-grid/src/query/DatasourceController.ts
index 5f5c5b0b1f..6fe2f5a66f 100644
--- a/packages/shared/widget-plugin-grid/src/query/DatasourceController.ts
+++ b/packages/shared/widget-plugin-grid/src/query/DatasourceController.ts
@@ -1,3 +1,4 @@
+import { disposeBatch } from "@mendix/widget-plugin-mobx-kit/disposeBatch";
import { DerivedPropsGate } from "@mendix/widget-plugin-mobx-kit/props-gate";
import { ReactiveController, ReactiveControllerHost } from "@mendix/widget-plugin-mobx-kit/reactive-controller";
import { ListValue, ValueStatus } from "mendix";
@@ -10,7 +11,7 @@ type DatasourceControllerSpec = { gate: Gate };
export class DatasourceController implements ReactiveController, QueryController {
private gate: Gate;
- private refreshing = false;
+ private backgroundCheck = false;
private fetching = false;
private pageSize = Infinity;
@@ -18,33 +19,40 @@ export class DatasourceController implements ReactiveController, QueryController
host.addController(this);
this.gate = spec.gate;
- type PrivateMembers = "resetFlags" | "updateFlags" | "setRefreshing" | "setFetching" | "pageSize";
+ type PrivateMembers =
+ | "resetFlags"
+ | "updateFlags"
+ | "setRefreshing"
+ | "setFetching"
+ | "pageSize"
+ | "setIsLoaded";
makeAutoObservable(this, {
setup: false,
pageSize: false,
updateFlags: action,
resetFlags: action,
setRefreshing: action,
- setFetching: action
+ setFetching: action,
+ setIsLoaded: action
});
}
private resetFlags(): void {
- this.refreshing = false;
+ this.backgroundCheck = false;
this.fetching = false;
}
private updateFlags(status: ValueStatus): void {
- if (this.refreshing) {
- this.setRefreshing(status === "loading");
+ if (this.backgroundCheck) {
+ this.setBackgroundCheck(status === "loading");
}
if (this.fetching) {
this.setFetching(status === "loading");
}
}
- private setRefreshing(value: boolean): void {
- this.refreshing = value;
+ private setBackgroundCheck(value: boolean): void {
+ this.backgroundCheck = value;
}
private setFetching(value: boolean): void {
@@ -63,15 +71,16 @@ export class DatasourceController implements ReactiveController, QueryController
return this.gate.props.datasource;
}
- get isLoading(): boolean {
- if (this.isRefreshing || this.isFetchingNextBatch) {
- return false;
- }
- return this.isDSLoading;
+ get isFirstLoad(): boolean {
+ return this.isDSLoading && this.datasource.items === undefined;
}
get isRefreshing(): boolean {
- return this.refreshing;
+ return this.isDSLoading && this.datasource.items !== undefined;
+ }
+
+ get isSilentRefresh(): boolean {
+ return this.backgroundCheck;
}
get isFetchingNextBatch(): boolean {
@@ -105,17 +114,23 @@ export class DatasourceController implements ReactiveController, QueryController
}
setup(): () => void {
- return autorun(() => {
- // Always use actions to set flags to avoid subscribing to them
- this.updateFlags(this.datasource.status);
- });
+ const [add, disposeAll] = disposeBatch();
+
+ add(
+ autorun(() => {
+ // Always use actions to set flags to avoid subscribing to them
+ this.updateFlags(this.datasource.status);
+ })
+ );
+
+ return disposeAll;
}
- refresh(): void {
+ backgroundRefresh(): void {
if (this.isDSLoading) {
return;
}
- this.setRefreshing(true);
+ this.setBackgroundCheck(true);
this.datasource.reload();
}
diff --git a/packages/shared/widget-plugin-grid/src/query/RefreshController.ts b/packages/shared/widget-plugin-grid/src/query/RefreshController.ts
index 51e9964b33..0df710f72f 100644
--- a/packages/shared/widget-plugin-grid/src/query/RefreshController.ts
+++ b/packages/shared/widget-plugin-grid/src/query/RefreshController.ts
@@ -2,7 +2,7 @@ import { autoEffect } from "@mendix/widget-plugin-mobx-kit/autoEffect";
import { ReactiveController, ReactiveControllerHost } from "@mendix/widget-plugin-mobx-kit/main";
interface QueryHelper {
- refresh(): void;
+ backgroundRefresh(): void;
}
interface ObservableAtom {
@@ -34,7 +34,7 @@ export class RefreshController implements ReactiveController {
private scheduleRefresh(helper: QueryHelper, delay: number): () => void {
const timerId = setTimeout(() => {
- helper.refresh();
+ helper.backgroundRefresh();
}, delay);
return () => {
clearTimeout(timerId);
diff --git a/packages/shared/widget-plugin-grid/src/query/query-controller.ts b/packages/shared/widget-plugin-grid/src/query/query-controller.ts
index bc20172a63..a5fb0421b3 100644
--- a/packages/shared/widget-plugin-grid/src/query/query-controller.ts
+++ b/packages/shared/widget-plugin-grid/src/query/query-controller.ts
@@ -12,10 +12,10 @@ type Members =
| "hasMoreItems";
export interface QueryController extends Pick {
- refresh(): void;
+ backgroundRefresh(): void;
setPageSize(size: number): void;
hasMoreItems: boolean;
- isLoading: boolean;
+ isFirstLoad: boolean;
isRefreshing: boolean;
isFetchingNextBatch: boolean;
}