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; }