feat: Update TableWidgetV2 to include customIsLoading property (#36857)

## Description
<ins>Problem</ins>
There are many problems with table loader logic, for which many users
try to implement a modal for loader. These problems stem from dependency
and delay on eval, discussed comprehensively in #12308

<ins>Solution</ins>
This PR updates the TableWidgetV2 component to include a new property
called `customIsLoading`. This property controls the loading state of
the widget and is added to the TableWidgetProps interface. Additionally,
the component's state is updated to include the `customIsLoading`
property.

The `contentConfig` file for the TableWidgetV2 is also modified to
include the `customIsLoading` property with its corresponding label,
control type, help text, and validation.

These changes improve the flexibility and customization options of the
TableWidgetV2 component.

Fixes #12308
_or_  
Fixes `Issue URL`
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.Table"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/11456273525>
> Commit: 1c6f4f9caabc3aa45ec3916e5ccb465d946ab0a1
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11456273525&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Table`
> Spec:
> <hr>Tue, 22 Oct 2024 09:17:37 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a new feature flag for custom loading states in the table
widget.
- Added properties for managing custom loading behavior in the
`TableWidgetV2`.
  
- **Bug Fixes**
- Enhanced loading state management to ensure accurate representation
based on new properties.

- **Tests**
- Added unit tests for loading behavior in the `TableWidgetV2`
component, covering default and custom loading scenarios.

- **Documentation**
- Updated help text for properties related to loading states to improve
clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Rahul Barwal 2024-10-22 14:51:45 +05:30 committed by GitHub
parent dc5fbed66b
commit 554ec58d40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 225 additions and 3 deletions

View File

@ -39,6 +39,8 @@ export const FEATURE_FLAG = {
release_anvil_toggle_enabled: "release_anvil_toggle_enabled",
release_git_persist_branch_enabled: "release_git_persist_branch_enabled",
release_ide_animations_enabled: "release_ide_animations_enabled",
release_table_custom_loading_state_enabled:
"release_table_custom_loading_state_enabled",
} as const;
export type FeatureFlag = keyof typeof FEATURE_FLAG;
@ -74,6 +76,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
release_anvil_toggle_enabled: false,
release_git_persist_branch_enabled: false,
release_ide_animations_enabled: false,
release_table_custom_loading_state_enabled: false,
};
export const AB_TESTING_EVENT_KEYS = {

View File

@ -109,6 +109,8 @@ export interface TableWidgetProps
firstEditableColumnIdByOrder: string;
enableServerSideFiltering: boolean;
onTableFilterUpdate: string;
customIsLoading: boolean;
customIsLoadingValue: boolean;
}
export enum TableVariantTypes {
@ -237,3 +239,6 @@ export const DEFAULT_COLUMN_NAME = "Table Column";
export const ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING =
FEATURE_FLAG["release_table_serverside_filtering_enabled"];
export const CUSTOM_LOADING_STATE_ENABLED =
FEATURE_FLAG["release_table_custom_loading_state_enabled"];

View File

@ -0,0 +1,181 @@
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
import TableWidgetV2 from ".";
import type { TableWidgetProps } from "../constants";
describe("TableWidgetV2 getWidgetView", () => {
const tableWidgetProps: TableWidgetProps = {
customIsLoading: false,
customIsLoadingValue: false,
delimiter: ",",
filteredTableData: [],
isVisibleDownload: true,
isVisibleFilters: true,
isVisiblePagination: true,
isVisibleSearch: true,
pageSize: 10,
primaryColumns: {},
totalRecordsCount: 100,
accentColor: "#000000",
borderColor: "#000000",
borderRadius: "40px",
borderWidth: "1px",
boxShadow: "none",
canFreezeColumn: true,
columnWidthMap: {},
compactMode: "DEFAULT",
filters: [],
isAddRowInProgress: false,
isEditableCellsValid: {},
isLoading: false,
isSortable: true,
multiRowSelection: false,
pageNo: 1,
renderMode: "CANVAS",
searchText: "",
selectedRowIndex: -1,
selectedRowIndices: [],
serverSidePaginationEnabled: false,
tableData: [],
widgetId: "widgetId",
widgetName: "TableWidget",
componentWidth: 800,
componentHeight: 600,
onPageChange: "",
onSearchTextChanged: "",
onSort: "",
onRowSelected: "",
onAddNewRowSave: "",
onAddNewRowDiscard: "",
onBulkSave: "",
onBulkDiscard: "",
onPageSizeChange: "",
commitBatchMetaUpdates: jest.fn(),
pushBatchMetaUpdates: jest.fn(),
updateWidgetMetaProperty: jest.fn(),
updateWidgetProperty: "",
updateOneClickBindingOptionsVisibility: "",
// Added missing properties
primaryColumnId: "",
columnOrder: [],
derivedColumns: {},
dynamicPropertyPathList: [],
dynamicTriggerPathList: [],
dynamicBindingPathList: [],
childStylesheet: {},
isVisible: true,
version: 1,
parentColumnSpace: 1,
parentRowSpace: 1,
leftColumn: 0,
rightColumn: 0,
topRow: 0,
bottomRow: 0,
parentId: "",
responsiveBehavior: ResponsiveBehavior.Hug,
minWidth: 0,
minHeight: 0,
isDisabled: false,
animateLoading: false,
primaryColor: "",
backgroundColor: "",
textColor: "",
fontFamily: "",
fontSize: "",
fontStyle: "",
textAlign: "",
textDecoration: "",
textTransform: "",
letterSpacing: "",
lineHeight: "",
whiteSpace: "",
overflow: "",
textOverflow: "",
wordBreak: "",
wordWrap: "",
cursor: "",
zIndex: 0,
pristine: true,
label: "TableWidget",
defaultSearchText: "",
sortOrder: { column: "", order: null },
transientTableData: { data: { name: "name" } },
newRow: {},
firstEditableColumnIdByOrder: "",
enableServerSideFiltering: false,
onTableFilterUpdate: "",
type: "",
allowAddNewRow: false,
defaultNewRow: {},
frozenColumnIndices: { a: 1 },
};
describe("TableWidgetV2 loading checks", () => {
describe("When custom loading logic is not provided", () => {
it("Should not be loading with built-in property isLoading is set to false", () => {
const tableWidget = new TableWidgetV2(tableWidgetProps);
const widgetView = tableWidget.getWidgetView();
expect(widgetView.props.children.props.isLoading).toBe(false);
});
it("Should be loading with built-in property isLoading is set to true", () => {
const tableWidget = new TableWidgetV2({
...tableWidgetProps,
isLoading: true,
});
const widgetView = tableWidget.getWidgetView();
expect(widgetView.props.children.props.isLoading).toBe(true);
});
});
describe("When custom loading logic is provided", () => {
describe("When isLoading is false", () => {
it("Should not be loading with isLoading: false, customIsLoading: true and customIsLoadingTrue: true", () => {
const tableWidget = new TableWidgetV2({
...tableWidgetProps,
customIsLoading: true,
customIsLoadingValue: false,
isLoading: false,
});
const widgetView = tableWidget.getWidgetView();
expect(widgetView.props.children.props.isLoading).toBe(false);
});
it("Should be loading with customIsLoading set to true and customIsLoadingTrue set to true", () => {
const tableWidget = new TableWidgetV2({
...tableWidgetProps,
customIsLoading: true,
customIsLoadingValue: true,
isLoading: false,
});
const widgetView = tableWidget.getWidgetView();
expect(widgetView.props.children.props.isLoading).toBe(true);
});
});
describe("When isLoading is true", () => {
it("Should be loading with customIsLoading set to true and customIsLoadingTrue set to false", () => {
const tableWidget = new TableWidgetV2({
...tableWidgetProps,
customIsLoading: true,
customIsLoadingValue: false,
isLoading: true,
});
const widgetView = tableWidget.getWidgetView();
expect(widgetView.props.children.props.isLoading).toBe(true);
});
it("Should be loading with customIsLoading set to true and customIsLoadingTrue set to true, even if in built loading is false", () => {
const tableWidget = new TableWidgetV2({
...tableWidgetProps,
customIsLoading: true,
customIsLoadingValue: true,
isLoading: true,
});
const widgetView = tableWidget.getWidgetView();
expect(widgetView.props.children.props.isLoading).toBe(true);
});
});
});
});
});

View File

@ -226,6 +226,8 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
)
? false
: undefined,
customIsLoading: false,
customIsLoadingValue: "",
};
}
@ -1211,6 +1213,8 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
getWidgetView() {
const {
customIsLoading,
customIsLoadingValue,
delimiter,
filteredTableData = [],
isVisibleDownload,
@ -1266,7 +1270,11 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
height={componentHeight}
isAddRowInProgress={this.props.isAddRowInProgress}
isEditableCellsValid={this.props.isEditableCellsValid}
isLoading={this.props.isLoading}
isLoading={
customIsLoading
? customIsLoadingValue || this.props.isLoading
: this.props.isLoading
}
isSortable={this.props.isSortable ?? true}
isVisibleDownload={isVisibleDownload}
isVisibleFilters={isVisibleFilters}

View File

@ -7,7 +7,10 @@ import { ValidationTypes } from "constants/WidgetValidation";
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType";
import type { TableWidgetProps } from "widgets/TableWidgetV2/constants";
import { ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING } from "../../constants";
import {
ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING,
CUSTOM_LOADING_STATE_ENABLED,
} from "../../constants";
import { InlineEditingSaveOptions } from "widgets/TableWidgetV2/constants";
import { composePropertyUpdateHook } from "widgets/WidgetUtils";
import {
@ -494,13 +497,35 @@ export default [
propertyName: "animateLoading",
label: "Animate loading",
controlType: "SWITCH",
helpText: "Controls the loading of the widget",
helpText: "Controls the animation loading of the widget",
defaultValue: true,
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.BOOLEAN },
},
{
propertyName: "customIsLoading",
label: `Custom loading state`,
controlType: "SWITCH",
helpText: "Defines a custom value for the loading state",
defaultValue: false,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.BOOLEAN },
hidden: () => !Widget.getFeatureFlag(CUSTOM_LOADING_STATE_ENABLED),
},
{
propertyName: "customIsLoadingValue",
label: "isLoading value",
controlType: "INPUT_TEXT",
defaultValue: "",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.BOOLEAN },
hidden: (props: TableWidgetProps) => !props.customIsLoading,
dependencies: ["customIsLoading"],
},
{
propertyName: "isVisibleDownload",
helpText: "Toggle visibility of the data download",