Skip to content
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

chore: refactor table widget UI code to use central context to reduce props drilling #39367

Open
wants to merge 26 commits into
base: release
Choose a base branch
from

Conversation

rahulbarwal
Copy link
Contributor

@rahulbarwal rahulbarwal commented Feb 19, 2025

Description

Refactor TableWidget for Improved Organization, Context Management & Rendering Efficiency

Overview

This PR restructures the TableWidget to enhance code organization, improve context management, and optimize rendering performance. Key improvements include extracting reusable components, introducing a shared TableContext, and simplifying table rendering logic.

Key Changes

✅ Improved Table Header Organization & Context Management
✅ Refactored Table & Virtualization Logic
✅ Simplified Header Components
✅ Enhanced Empty Row Handling
✅ Reorganized Core Components(Static table, virtual table)

Why These Changes?

•	Improves maintainability and readability by reducing prop drilling and redundant code.
•	Enhances performance through better state management and rendering optimizations.
•	Provides a scalable structure for future improvements in the TableWidget.

Fixes #39308
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, @tag.Sanity, @tag.Datasource"

🔍 Cypress test results

Tip

🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/13761069297
Commit: 524a846
Cypress dashboard.
Tags: @tag.Table, @tag.Sanity, @tag.Datasource
Spec:


Mon, 10 Mar 2025 12:31:27 UTC

Communication

Should the DevRel and Marketing teams inform users about this change?

  • Yes
  • No

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced new virtualized table components that enhance dynamic scrolling and table interactions.
    • Added a new table constant to ensure smoother scrolling behavior.
    • Implemented a centralized table state provider to consolidate table functionality.
    • Added a new BannerNActions component for improved header action management.
    • Added a new StaticTableBodyComponent for rendering static table bodies.
  • Refactor

    • Streamlined static table rendering along with header and cell interactions.
    • Simplified component interfaces by shifting from prop-based to context-driven state management.
    • Enhanced the Actions component to utilize context for state management.
    • Refactored the Table component to improve clarity and efficiency.
  • Tests

    • Expanded and improved test coverage to ensure robust rendering, error handling, and performance.
    • Introduced tests for new components such as StaticTableBodyComponent and BannerNActions.
    • Updated tests to reflect changes in context management and component structure.

…management

- Extracts table header logic into separate components
- Introduces TableProvider context to manage table state
- Moves types to separate file for better organization
- Adds default compact mode fallback
- Simplifies imports and reorganizes code structure
- Moves table state management to TableContext
- Adds memoization for computed values
- Extracts scroll container styles to context
- Simplifies StaticTable component props
- Adds SCROLL_BAR_OFFSET constant
- Removes redundant SimpleBar wrapper in Table component
- Adds SimpleBar directly in TableHeader for consistent scrolling
- Imports SimpleBar CSS for proper styling
…ering

- Refactors VirtualTable to use TableContext for props
- Simplifies Table component's conditional rendering
- Removes redundant prop passing in VirtualTable
- Adds type definition for StaticTable props
- Refactors HeaderCell to use TableContext for accessing shared props
- Removes redundant prop passing in HeaderCell
- Simplifies header cell rendering and interaction logic
- Improves component readability and reduces prop drilling
- Removes explicit prop passing in TableColumnHeader
- Uses TableContext to access shared table state
- Calculates header width using useMemo
- Simplifies header rendering and interaction logic
- Refactors renderEmptyRows to use TableContext for accessing table state
- Removes explicit prop passing in EmptyRows and EmptyRow components
- Simplifies empty row rendering logic
- Improves component readability and reduces prop drilling
Copy link
Contributor

coderabbitai bot commented Feb 19, 2025

Walkthrough

This pull request refactors the table widget code by centralizing the state management into a new TableContext and associated hook (useAppsmithTable). Components across static, virtual, header, body, and cell parts are updated to remove redundant props and interfaces. Several components have been removed or replaced with new implementations that use the context, and tests have been added or updated to cover the new functionality and workflows.

Changes

File(s) Change Summary
.../Constants.ts, .../types.ts Added SCROLL_BAR_OFFSET = 2 in Constants; new interfaces TableProps and TableProviderProps defined in types.
.../StaticTable.tsx, .../StaticTable/index.tsx, .../StaticTable/StaticTableBodyComponent.tsx, .../__tests__/StaticTable*.tsx Removed original StaticTable component; introduced new StaticTable (with ref forwarding) and StaticTableBodyComponent; added tests.
.../Table.tsx, .../TableContext.tsx, .../TableContext.test.tsx Simplified Table component by removing TableProps; added TableProvider and useAppsmithTable hook; new tests for context.
.../TableBody/index.tsx, .../TableBodyCoreComponents/Row.tsx, .../TableBodyCoreComponents/VirtualList.tsx Removed TableBody component and related entities; in Row.tsx, eliminated EmptyRows/EmptyRow functions in favor of hook; updated VirtualList prop types (required outerRef, replaced EmptyRow with EmptyRows).
.../VirtualTable.tsx, .../VirtualTable/InifiniteScrollBody/index.tsx, .../VirtualTable/VirtualTableBodyComponent.tsx, .../VirtualTable/VirtualTableInnerElement.tsx, .../VirtualTable/index.tsx Removed old VirtualTable; renamed InfiniteScrollBody to InfiniteScrollBodyComponent with hook-based props; introduced new VirtualTableBodyComponent and VirtualTableInnerElement; added a new VirtualTable supporting dynamic rendering based on infinite scroll flag.
.../cellComponents/EmptyCell.tsx, .../cellComponents/HeaderCell.tsx Refactored EmptyCell: renamed renderEmptyRows to EmptyRows and streamlined implementation; HeaderCell now uses context hook, eliminating redundant props.
.../header/* (BannerNActions.tsx, TableColumnHeader.tsx, index.tsx, banner/index.tsx, actions/index.tsx, actions/Actions.test.tsx) Added new BannerNActions component; refactored TableColumnHeader, TableHeader, BannerComponent, and Actions to use useAppsmithTable instead of props; updated associated tests.
.../index.tsx Reorganized imports and updated the Table component’s compactMode to default using CompactModeTypes.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant VT as VirtualTable
  participant UA as useAppsmithTable
  participant ISC as InfiniteScrollBodyComponent
  participant VTB as VirtualTableBodyComponent
  participant SB as SimpleBar

  U->>VT: Render Table
  VT->>UA: Fetch table config and state
  alt Infinite scroll enabled
    VT->>ISC: Render Infinite Scroll Body
  else
    VT->>VTB: Render Virtual Table Body
  end
  VT->>SB: Wrap content in SimpleBar
  SB-->>U: Display scrollable table view
Loading

Suggested labels

Enhancement, Task, Needs Triaging

Suggested reviewers

  • jacquesikot
  • AmanAgarwal041

Poem

In code fields where logic flows,
New contexts rise and structure grows.
Tables realigned with cleaner art,
Refactored pieces set apart.
May our tests pass with joyful light—
A refined widget sings day and night! 😊


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between a972f6d and 524a846.

📒 Files selected for processing (1)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableBodyComponent.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableBodyComponent.tsx
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: perform-test / server-build / server-unit-tests
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: client-prettier / prettier-check
  • GitHub Check: client-build / client-build
  • GitHub Check: client-lint / client-lint
  • GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added Query & Widgets Pod All issues related to Query, JS, Eval, and Widgets Table Widget V2 Issues related to Table Widget V2 Widgets & Accelerators Pod Issues related to widgets & Accelerators Widgets Product This label groups issues related to widgets skip-changelog Adding this label to a PR prevents it from being listed in the changelog labels Feb 19, 2025
@rahulbarwal rahulbarwal self-assigned this Feb 21, 2025
@rahulbarwal rahulbarwal marked this pull request as ready for review February 21, 2025 09:44
@rahulbarwal rahulbarwal added the ok-to-test Required label for CI label Feb 21, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (16)
app/client/src/widgets/TableWidgetV2/component/header/index.tsx (3)

13-49: Refactor for Enhanced Readability

The direct extraction of many properties from useAppsmithTable() can become unwieldy. Consider grouping related properties into sub-objects to improve clarity and reduce repetitive prop spreads throughout the code.


59-77: Check Scrolling Behavior

Wrapping with SimpleBar is a neat approach to manage scrolling. Validate that scroll events and styles are correctly handled in all browsers. A fallback for environments without custom scrollbar support could further improve UX.


78-112: Consider Bundling Props

BannerNActions receives numerous props, which might be streamlined using an object or a context. This can help reduce repetitive passing of props and improve maintainability.

app/client/src/widgets/TableWidgetV2/component/header/BannerNActions.tsx (2)

7-16: Leverage TypeScript Union Types

Explicitly defining a union type for scenarios like isAddRowInProgress can create clearer, narrower prop contracts. Consider applying such patterns to better communicate the intended usage.


18-34: Centralize Conditional Logic

Conditional rendering is handled cleanly. If more states and banner types emerge in the future, moving conditional logic into a dedicated helper function or a switch-based approach may improve scalability.

app/client/src/widgets/TableWidgetV2/component/cellComponents/HeaderCell.tsx (2)

177-178: Offset logic for sticky column with multi-row selection.
Ensure props.column.totalLeft is always a valid numeric value to avoid misaligned headers.


204-205: Optional chaining to handle freeze.
If handleColumnFreeze will always be defined from the hook, consider removing the guard.

🧰 Tools
🪛 Biome (1.9.4)

[error] 204-208: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/client/src/widgets/TableWidgetV2/component/Table.tsx (1)

133-136: Confirm behavior for out-of-range pageNo.
If props.pageNo exceeds pageCount, resetting to 0 may be unexpected. Consider clamping to the last page instead if that's the desired user experience.

app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx (1)

8-8: Use a type alias instead of an empty interface.
Empty interfaces are equivalent to {}. Updating with a type alias improves clarity:

-interface StaticTableProps {}
+type StaticTableProps = Record<string, never>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 7-8: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (1)

36-36: Extract magic number into a named constant.

The buffer size of 5 should be defined as a named constant to improve code maintainability and clarity.

+const ITEM_COUNT_BUFFER = 5;

-          itemCount={itemCount + 5}
+          itemCount={itemCount + ITEM_COUNT_BUFFER}
app/client/src/widgets/TableWidgetV2/component/TableContext.tsx (2)

76-84: Update error message to match hook name.

The error message refers to "useTable" but the hook is named "useAppsmithTable".

-    throw new Error("useTable must be used within a TableProvider");
+    throw new Error("useAppsmithTable must be used within a TableProvider");

54-65: Optimize memoization dependencies.

The scrollContainerStyles memo dependency on state.compactMode is unnecessary since tableSizes is already derived from it.

  }, [
    state.isHeaderVisible,
    state.height,
-   state.compactMode,
    state.width
  ]);
app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/Row.tsx (2)

67-99: Extract cell rendering logic to a separate component.

The cell rendering logic is complex and could benefit from being extracted into a separate component for better maintainability and potential reuse.

+ const TableCell = React.memo(({ cell, cellIndex, columns, multiRowSelection }) => {
+   const cellProperties = cell.getCellProps();
+   cellProperties["style"] = {
+     ...cellProperties.style,
+     left:
+       columns[cellIndex].sticky === StickyType.LEFT && multiRowSelection
+         ? cell.column.totalLeft + MULTISELECT_CHECKBOX_WIDTH
+         : cellProperties?.style?.left,
+   };
+   return (
+     <div
+       {...cellProperties}
+       className={/* ... existing className logic ... */}
+       data-colindex={cellIndex}
+       data-rowindex={props.index}
+       key={cellIndex}
+     >
+       {cell.render("Cell")}
+     </div>
+   );
+ });

32-38: Memoize row style computation.

The row style computation could be memoized to prevent unnecessary recalculations.

+ const rowStyle = React.useMemo(
+   () => ({
+     display: "flex",
+     ...(props.style || {}),
+   }),
+   [props.style]
+ );
  const rowProps = {
    ...props.row.getRowProps(),
-   style: {
-     display: "flex",
-     ...(props.style || {}),
-   },
+   style: rowStyle,
  };
app/client/src/widgets/TableWidgetV2/component/header/TableColumnHeader.tsx (1)

113-113: Add error boundary for empty rows rendering

Consider wrapping the empty rows rendering with an error boundary to gracefully handle potential rendering failures.

app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (1)

127-154: Optimize sticky column calculations

Consider memoizing the sticky column calculations to prevent unnecessary recalculations during re-renders.

+const calculateLeftDistance = React.useMemo(() => {
   const leftColWidths = tableColumns
     .slice(0, colIndex)
     .map((col) => col.width);

   if (multiRowSelection) {
     return colIndex === 0
       ? MULTISELECT_CHECKBOX_WIDTH
       : sum(leftColWidths) + MULTISELECT_CHECKBOX_WIDTH;
   }
   return colIndex === 0 ? 0 : sum(leftColWidths);
+}, [colIndex, multiRowSelection, tableColumns]);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98457f9 and 6e90439.

📒 Files selected for processing (22)
  • app/client/src/widgets/TableWidgetV2/component/Constants.ts (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable.tsx (0 hunks)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/StaticTableBody/index.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/Table.tsx (7 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableBody/index.tsx (0 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/Row.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/VirtualList.tsx (2 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableContext.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable.tsx (0 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (2 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableBodyComponent.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableInnerElement.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/index.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (2 hunks)
  • app/client/src/widgets/TableWidgetV2/component/cellComponents/HeaderCell.tsx (9 hunks)
  • app/client/src/widgets/TableWidgetV2/component/header/BannerNActions.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/header/TableColumnHeader.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/header/VirtualTableInnerElement.tsx (0 hunks)
  • app/client/src/widgets/TableWidgetV2/component/header/index.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/index.tsx (2 hunks)
  • app/client/src/widgets/TableWidgetV2/component/types.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable.tsx
  • app/client/src/widgets/TableWidgetV2/component/StaticTable.tsx
  • app/client/src/widgets/TableWidgetV2/component/TableBody/index.tsx
  • app/client/src/widgets/TableWidgetV2/component/header/VirtualTableInnerElement.tsx
✅ Files skipped from review due to trivial changes (1)
  • app/client/src/widgets/TableWidgetV2/component/Constants.ts
🧰 Additional context used
🪛 Biome (1.9.4)
app/client/src/widgets/TableWidgetV2/component/VirtualTable/index.tsx

[error] 8-9: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx

[error] 7-8: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: perform-test / client-build / client-build
  • GitHub Check: perform-test / server-build / server-unit-tests
🔇 Additional comments (30)
app/client/src/widgets/TableWidgetV2/component/header/index.tsx (1)

51-56: Validate Memo Dependencies

Using React.useMemo here is optimal. However, ensure that all sources influencing columns are properly included in its dependency array. If columns is derived from dynamic data, confirm that the memo remains valid on each relevant update.

app/client/src/widgets/TableWidgetV2/component/header/BannerNActions.tsx (1)

1-5: Verify Import Paths

The new BannerNActions component correctly imports from actions and banner. Ensure that any rename or path relocations get updated consistently across the codebase.

app/client/src/widgets/TableWidgetV2/component/cellComponents/HeaderCell.tsx (11)

159-169: Great use of the central hook for table props.
This centralizes state effectively and simplifies prop management.


172-172: No issue with spreading header props.
This is a standard approach and looks good.


182-182: Confirm isResizingColumn type.
Ensure isResizingColumn is a ref to avoid runtime errors calling .current.


193-193: Validate negative index usage for sorting.
Check if the sortTableColumn(-1, ...) call gracefully handles "no sort" scenarios.


199-201: Readability of editable checks.
This chain of conditions is clear and concise.


274-276: Horizontal alignment logic is clear.
No issues in referencing columnProperties.horizontalAlignment.


286-286: Menu visibility condition.
The condition for hiding the menu is straightforward and looks correct.


296-306: Sorting actions on menu items.
Both ascending and descending sort calls appear consistent.


317-321: Freeze column menu items.
Conditional disabling and check icons are used effectively.

Also applies to: 329-333


347-347: Portal class referencing the widget ID.
This namespacing strategy is good for preventing clashes.


365-366: Resizer props integration.
The resizing logic is properly delegated to getResizerProps().

app/client/src/widgets/TableWidgetV2/component/Table.tsx (9)

1-3: Imports look good.
No issues noticed. The new references to Blueprint libraries, React Table, and related modules are appropriately declared.

Also applies to: 7-7, 9-15, 17-17, 21-21, 23-24, 27-27, 29-30


120-132: Check for zero or negative page sizes.
Consider verifying if props.pageSize can be zero or negative, which could lead to unexpected logic or infinite pageCount scenarios.


214-224: Confirm partial row selection toggle logic.
toggleAllRowSelect(!Boolean(rowSelectionState), page) also clears partial selections (state = 2). Double-check that this aligns with the intended tri-state selection behavior.

🧰 Tools
🪛 Biome (1.9.4)

[error] 219-219: Avoid redundant Boolean call

It is not necessary to use Boolean call when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant Boolean call

(lint/complexity/noExtraBooleanCast)


224-239: Header visibility logic is clear.
Memoizing the condition for showing the header based on search, filters, and other flags is straightforward and readable.


255-269: Good approach to determining virtualization.
Leveraging isInfiniteScrollEnabled or checking for HTML columns is a clear strategy for deciding between static rendering and virtual scrolling.


282-296: Proper centralization with TableProvider.
Handing off table-specific props and state to a provider nicely simplifies the rest of the component.


326-326: Conditional table header is well-structured.
Showing the header only when necessary avoids clutter.


338-338: Swapping between VirtualTable and StaticTable is neat.
This ternary-based approach cleanly handles different rendering modes.


342-342: No additional remarks.

app/client/src/widgets/TableWidgetV2/component/StaticTable/StaticTableBody/index.tsx (1)

1-21: StaticTableBodyComponent is straightforward.
Mapping over rows to render a Row for each entry, and rendering EmptyRows if pageSize exceeds the number of rows, is efficient and clear.

app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx (1)

1-7: StaticTable implementation looks good.
Forwarding the ref to SimpleBar and composing column headers with the table body is well-structured and easy to follow.

Also applies to: 9-21

app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableBodyComponent.tsx (1)

1-30: LGTM! Clean implementation with proper separation of concerns.

The component follows React best practices by using forwardRef and centralizing state management through the useAppsmithTable hook.

app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/VirtualList.tsx (1)

37-37:

❓ Verification inconclusive

Breaking Change: outerRef is now required.

Making outerRef required could break existing implementations that don't provide this prop. Ensure all consumers of this component are updated.


🏁 Script executed:

#!/bin/bash
# Find all usages of BaseVirtualListProps or VirtualList components
ast-grep --pattern 'type $_ = BaseVirtualListProps'
ast-grep --pattern 'const $_ = <(FixedVirtualList|FixedInfiniteVirtualList)'

Length of output: 76644


Action Required: Verify Consumers for outerRef Update

The change making outerRef: Ref<SimpleBar> required in VirtualList (line 37) is a breaking change that could impact any consumer not supplying this prop. Our shell searches did not reveal any clear usages of BaseVirtualListProps or the FixedVirtualList/FixedInfiniteVirtualList components, so we couldn’t confirm whether current consumers cover this new requirement. Please ensure that:

  • Every instance of VirtualList / its wrapped components is updated to pass a valid outerRef.
  • Manual verification is done for all consumers (particularly within TableWidgetV2) to prevent runtime errors.
app/client/src/widgets/TableWidgetV2/component/header/TableColumnHeader.tsx (2)

16-29: Well-structured state management using context!

The refactoring successfully centralizes state management by using the useAppsmithTable hook, which reduces prop drilling and improves code organization.


30-36:

❓ Verification inconclusive

Verify handling of zero totalColumnsWidth

While the memoization is correct, verify that the component handles cases where totalColumnsWidth is 0 appropriately.


🏁 Script executed:

#!/bin/bash
# Search for tests or handling of zero totalColumnsWidth
rg -A 5 "totalColumnsWidth.*[=><].*0" 

Length of output: 39


Verify zero totalColumnsWidth edge case handling

The memoization implementation is correct; however, no explicit tests or handling for when totalColumnsWidth is 0 were found in the repository. Please verify manually that when totalColumnsWidth is 0, the component computes headerWidth as expected (i.e., it should remain 0 rather than unexpectedly applying the multi-selection width). If this is an important edge case, consider adding a dedicated unit test to cover this behavior.

app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (1)

23-32: Clean function signature refactoring!

The function has been successfully simplified by leveraging the context hook while maintaining its core functionality.

app/client/src/widgets/TableWidgetV2/component/index.tsx (1)

238-238: Good default value handling!

Adding a default value for compactMode ensures consistent behavior when the prop is not provided.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTable.test.tsx (2)

8-21: Consider adding error cases to SimpleBar mock.

The SimpleBar mock is well-structured but only handles the happy path. Consider adding test cases for error scenarios.

 jest.mock("simplebar-react", () => {
   return {
     __esModule: true,
     default: React.forwardRef<
       HTMLDivElement,
       { children: React.ReactNode; style?: React.CSSProperties }
     >((props, ref) => (
+      // Add error handling for invalid props
+      !props ? null : 
       <div className="simplebar-content-wrapper" ref={ref} style={props.style}>
         {props.children}
       </div>
     )),
   };
 });

55-62: Add test for table rendering with empty data.

The test suite covers basic rendering but misses edge cases. Consider adding a test for rendering with empty data.

it("renders correctly with empty data", () => {
  (useAppsmithTable as jest.Mock).mockReturnValue({
    scrollContainerStyles: { height: 0, width: 0 },
  });
  const ref = React.createRef<SimpleBar>();
  render(<StaticTable ref={ref} />);
  expect(screen.getByTestId("mock-table-header")).toBeInTheDocument();
  expect(screen.getByTestId("mock-table-body")).toBeInTheDocument();
});
app/client/src/widgets/TableWidgetV2/component/TableContext.test.tsx (1)

287-308: Enhance memoization test with multiple re-renders.

The current memoization test only checks a single re-render. Consider testing multiple re-renders and prop changes.

it("maintains memoization across multiple re-renders", () => {
  const { rerender } = render(
    <TableProvider {...mockTableProviderProps}>
      <TestParent />
    </TableProvider>
  );

  const firstText = screen.getByText(/.+/).textContent;

  // Multiple re-renders with same props
  for (let i = 0; i < 5; i++) {
    rerender(
      <TableProvider {...mockTableProviderProps}>
        <TestParent />
      </TableProvider>
    );
    expect(screen.getByText(/.+/).textContent).toBe(firstText);
  }

  // Re-render with different props
  rerender(
    <TableProvider {...{ ...mockTableProviderProps, width: 900 }}>
      <TestParent />
    </TableProvider>
  );
  expect(screen.getByText(/.+/).textContent).not.toBe(firstText);
});
app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTableBody.test.tsx (1)

122-136: Enhance performance test with timing measurements.

The performance test verifies rendering of large datasets but doesn't measure actual performance metrics.

it("[Performance] should render large datasets within performance budget", () => {
  const largeDataSet = Array.from({ length: 1000 }, (_, index) =>
    createMockRow(index, index)
  );

  (useAppsmithTable as jest.Mock).mockReturnValue({
    ...mockTableContextState,
    pageSize: 1000,
    subPage: largeDataSet,
  } as unknown as TableContextState);

  const start = performance.now();
  const { getAllByTestId } = render(<StaticTableBodyComponent />);
  const end = performance.now();

  expect(getAllByTestId("mocked-row")).toHaveLength(1000);
  expect(end - start).toBeLessThan(100); // 100ms budget
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e90439 and 6b200f7.

📒 Files selected for processing (3)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTable.test.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTableBody.test.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableContext.test.tsx (1 hunks)
🔇 Additional comments (1)
app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTableBody.test.tsx (1)

30-37: LGTM! Well-structured test helper function.

The createMockRow helper function is well-implemented and reusable across tests.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (1)

167-169: Use optional chaining for safer property access.

The current implementation may cause runtime errors if column is undefined.

-                      column && column.isHidden
+                      column?.isHidden
🧰 Tools
🪛 Biome (1.9.4)

[error] 167-167: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03470d8 and f3dcf29.

📒 Files selected for processing (3)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/Row.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/header/actions/Actions.test.tsx (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/Row.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx

[error] 167-167: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: perform-test / ci-test / ci-test (19)
  • GitHub Check: perform-test / ci-test / ci-test (18)
  • GitHub Check: perform-test / ci-test / ci-test (17)
  • GitHub Check: perform-test / ci-test / ci-test (16)
  • GitHub Check: perform-test / ci-test / ci-test (15)
  • GitHub Check: perform-test / ci-test / ci-test (14)
  • GitHub Check: perform-test / ci-test / ci-test (13)
  • GitHub Check: perform-test / ci-test / ci-test (12)
  • GitHub Check: perform-test / ci-test / ci-test (11)
  • GitHub Check: perform-test / ci-test / ci-test (10)
  • GitHub Check: perform-test / ci-test / ci-test (9)
  • GitHub Check: perform-test / ci-test / ci-test (8)
  • GitHub Check: perform-test / ci-test / ci-test (7)
  • GitHub Check: perform-test / ci-test / ci-test (6)
  • GitHub Check: perform-test / ci-test / ci-test (5)
  • GitHub Check: perform-test / ci-test / ci-test (4)
  • GitHub Check: perform-test / ci-test / ci-test (3)
  • GitHub Check: perform-test / ci-test / ci-test (2)
  • GitHub Check: perform-test / ci-test / ci-test (1)
  • GitHub Check: perform-test / ci-test / ci-test (0)
🔇 Additional comments (16)
app/client/src/widgets/TableWidgetV2/component/header/actions/Actions.test.tsx (12)

5-6: Good use of the new TableProvider context.

The imports for TableProvider and its type definition look correct. This aligns with the PR's objective to use a central context instead of props drilling.


39-142: Comprehensive table provider props setup.

The defaultProps have been properly updated to match the TableProviderProps interface. This ensures the tests have all the necessary context values available.

A couple of observations:

  • All required props for the TableProvider are included
  • Mock functions are provided for all callbacks
  • Default state values align with typical table initial state

144-152: Clean testing helper function implementation.

The renderWithTableProvider function effectively encapsulates the boilerplate code for rendering the Actions component within the context providers. This improves test readability and reduces duplication.


155-155: Test case simplification through context usage.

The test case now uses the helper function which simplifies the test setup. This is more maintainable than the previous approach.


160-161: Proper test for conditional rendering.

The test correctly verifies that the search component is not rendered when isVisibleSearch is false.


167-167: Effective mock function passing.

The test correctly overrides the searchTableData function to verify it's called with the right parameters.


179-184: Proper pagination test setup.

The test correctly overrides multiple properties to test pagination with the right data and configuration.


194-199: Well structured navigation testing.

The test properly configures the context for testing the next page navigation functionality.


214-221: Comprehensive previous page navigation test.

The test effectively sets up the initial state needed to test the previous page functionality, including currentPageIndex and pageNo.


234-236: Simplified feature flag testing.

The test for the "add new row" feature correctly uses the context to enable this functionality.


244-247: Appropriate infinite scroll configuration.

The test correctly sets up the infinite scroll configuration with test data to verify record counting behavior.


256-260: Complete record count test with totalRecordsCount.

The test properly configures both the data array and totalRecordsCount to verify the correct display of record counts with infinite scrolling.

app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (4)

4-7: Updated imports to align with hook-based pattern.

The imports now reflect the shift to using context through the useAppsmithTable hook, which is consistent with reducing props drilling as mentioned in the PR objectives.


23-32: Function signature simplified with context pattern.

The function has been refactored to use the context-based pattern, reducing the number of parameters from 8 to just 2. This matches the PR goal of improving context management and reducing prop drilling.


39-93: Improved return structure using React fragments.

Returning a React fragment instead of an array provides a more idiomatic React approach while maintaining the same rendering functionality. This change also improves readability.


107-183: Same pattern applied to the empty state rendering.

The empty state rendering now consistently uses React fragments and leverages the table context, maintaining parallelism with the previous code block.

🧰 Tools
🪛 Biome (1.9.4)

[error] 167-167: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTableBody.test.tsx (2)

127-141: Performance test could be more meaningful.

While you're testing with 1000 rows, this test only verifies that rendering completes without error, not actual performance metrics. Consider adding more assertions around rendering time or memory usage.

 it("[Performance] should maintain rendering performance with large data sets", () => {
   const largeDataSet = Array.from({ length: 1000 }, (_, index) =>
     createMockRow(index, index),
   );

   (useAppsmithTable as jest.Mock).mockReturnValue({
     ...mockTableContextState,
     pageSize: 1000,
     subPage: largeDataSet,
   } as unknown as TableContextState);

+  const start = performance.now();
   const { getAllByTestId } = render(<StaticTableBodyComponent />);
+  const end = performance.now();

   expect(getAllByTestId("mocked-row")).toHaveLength(1000);
+  // Set a reasonable threshold based on your performance requirements
+  expect(end - start).toBeLessThan(500); // Example: render should take less than 500ms
 });

1-262: Consider adding accessibility tests.

The test suite is comprehensive for functional aspects but doesn't cover accessibility concerns. Consider adding tests that verify accessibility attributes.

You could add a test like this:

it("should have appropriate accessibility attributes", () => {
  const mockRows = [createMockRow(1, 0), createMockRow(2, 1)];
  
  (useAppsmithTable as jest.Mock).mockReturnValue({
    ...mockTableContextState,
    pageSize: 2,
    subPage: mockRows,
  } as unknown as TableContextState);
  
  const { container } = render(<StaticTableBodyComponent />);
  
  // Update these expectations based on your actual accessibility requirements
  const tbody = container.querySelector('.tbody');
  expect(tbody).toHaveAttribute('role', 'rowgroup');
});
app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (2)

115-191: Empty table rows logic.
The approach for rendering placeholders when page.length is zero is thorough. Consider extracting repeated sticky-column calculations into a helper to reduce complexity.

🧰 Tools
🪛 Biome (1.9.4)

[error] 175-175: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


175-175: Use optional chaining to avoid null checks.
Replacing column && column.isHidden with column?.isHidden can simplify the logic.

- className={
-   column && column.isHidden
-     ? "td hidden-cell"
-     : "td" + addStickyModifierClass(columns, cellIndex)
- }
+ className={
+   column?.isHidden
+     ? "td hidden-cell"
+     : "td" + addStickyModifierClass(columns, cellIndex)
+ }
🧰 Tools
🪛 Biome (1.9.4)

[error] 175-175: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/client/src/widgets/TableWidgetV2/component/header/actions/index.tsx (3)

245-249: Simplify pagination conditional logic

The conditional logic for disabling the next button and the conditional inside the onClick handler are redundant and can be simplified.

- disabled={!!totalRecordsCount && pageNo === pageCount - 1}
- onClick={() => {
-   if (!(!!totalRecordsCount && pageNo === pageCount - 1))
-     nextPageClick();
- }}
+ disabled={!!totalRecordsCount && pageNo === pageCount - 1}
+ onClick={nextPageClick}

269-274: Simplify page navigation logic

The current implementation calculates pageNo and then has a separate condition to check if navigation should occur. This can be simplified.

- onClick={() => {
-   const pageNo = currentPageIndex > 0 ? currentPageIndex - 1 : 0;
-
-   !(currentPageIndex === 0) &&
-     updatePageNo(pageNo + 1, EventType.ON_PREV_PAGE);
- }}
+ onClick={() => {
+   if (currentPageIndex > 0) {
+     updatePageNo(currentPageIndex, EventType.ON_PREV_PAGE);
+   }
+ }}

296-301: Simplify next page navigation logic

Similar to the previous comment, this logic can be simplified for better readability.

- onClick={() => {
-   const pageNo =
-     currentPageIndex < pageCount - 1 ? currentPageIndex + 1 : 0;
-
-   !(currentPageIndex === pageCount - 1) &&
-     updatePageNo(pageNo + 1, EventType.ON_NEXT_PAGE);
- }}
+ onClick={() => {
+   if (currentPageIndex < pageCount - 1) {
+     updatePageNo(currentPageIndex + 2, EventType.ON_NEXT_PAGE);
+   }
+ }}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3dcf29 and 9c53de0.

📒 Files selected for processing (7)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/StaticTableBody/index.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTableBody.test.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/Row.tsx (2 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/VirtualList.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/header/TableColumnHeader.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/header/actions/index.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/StaticTableBody/index.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx

[error] 175-175: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: perform-test / client-build / client-build
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: perform-test / server-build / server-unit-tests
  • GitHub Check: client-build / client-build
  • GitHub Check: client-lint / client-lint
  • GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
  • GitHub Check: client-unit-tests / client-unit-tests
  • GitHub Check: client-prettier / prettier-check
🔇 Additional comments (26)
app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTableBody.test.tsx (7)

1-11: Good import organization and mocking setup.

The imports are well-organized and all the necessary dependencies have been included.


12-28: Well-structured mocks for isolated component testing.

The mocks for TableContext, Row component, and EmptyRows provide good isolation for the component under test. Clean implementation of mock functions with appropriate return values.


30-53: Comprehensive test context setup.

Good approach for creating mock data and setup. The createMockRow helper function is an efficient way to generate test data.


55-69: Good error boundary testing.

Proper implementation of error case testing, including console error suppression to keep test output clean.


113-126: Good edge case handling for pageSize=0.

Testing the edge case where pageSize is 0 ensures the component behaves correctly in all scenarios.


173-200: Well-implemented test for dynamic updates.

The test correctly validates that the component updates properly when the data changes, an important aspect for reactive components.


203-260: Comprehensive EmptyRows testing.

All the necessary edge cases for empty row rendering are covered with clear assertions.

app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/Row.tsx (3)

1-2: Imports look good.
No issues found with importing Key from "react" and React for component usage.


7-7: Context usage updated.
Importing useAppsmithTable aligns with the new approach of using hooks for state management.


28-28: Destructuring from useAppsmithTable.
The destructured properties match usage in the component. Good job keeping the interface clean.

app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/VirtualList.tsx (5)

3-3: Typed ref import.
Great job leveraging type Ref from React for stronger typing.


7-7: Switching to the new Row component.
Referencing the Row component here is consistent with the refactor to a centralized hook-based context.


10-10: Introducing EmptyRows.
Replacing older empty row implementations with EmptyRows unifies empty state handling.


28-28: Rendering a single empty row.
Returning exactly one empty row with rows={1} is a neat fallback, but ensure it's the intended design for leftover space in the virtual list.


38-38: Required outerRef.
Making outerRef mandatory enforces consistent usage. Verify all call sites now supply it to avoid runtime errors.

app/client/src/widgets/TableWidgetV2/component/cellComponents/EmptyCell.tsx (5)

4-4: Refined “Cell” import.
Using Cell from "react-table" aligns with typed usage in the new structure.


7-7: Hook-based table access.
Importing useAppsmithTable enables consistent and centralized state retrieval.


23-26: New EmptyRowsProps interface.
Defining a dedicated interface improves clarity and type safety for the EmptyRows component.


28-40: Functional component for empty rows.
Adopting a functional component with useAppsmithTable is clean and consistent.


47-101: Reusing first row for styling.
Rendering additional empty rows by referencing the first row’s structure is clever. Just confirm that page[0] is always valid in these scenarios.

app/client/src/widgets/TableWidgetV2/component/header/TableColumnHeader.tsx (4)

2-8: Import organization looks clean

Good job consolidating the imports and adding the useAppsmithTable hook from TableContext.


18-31: Good transition to context-based state management

The component now properly uses the useAppsmithTable hook to access shared state, eliminating the need for props drilling. This aligns well with the PR objectives.


33-39: Efficient memoization implementation

Good use of useMemo for headerWidth calculation with the correct dependency array, preventing unnecessary recalculations.


114-114: Simplified empty rows rendering

The rendering of empty rows has been simplified, which improves readability.

app/client/src/widgets/TableWidgetV2/component/header/actions/index.tsx (2)

93-124: Effective transition to context-based approach

The component successfully migrates from a props-based approach to using context via useAppsmithTable, which reduces prop drilling as intended in the PR.


126-132: Proper memoization of derived data

Good use of useMemo for headerColumns with an appropriate dependency array, which prevents unnecessary recalculations.

Comment on lines 80 to 82
{/* TODO: Fix this the next time the file is edited */}
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
{headerGroup.headers.map((column: any, columnIndex: number) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider addressing the existing TODO comment

There's a TODO comment about fixing something "the next time the file is edited." Since you're already refactoring this file, now would be a good time to address this issue.


🌐 Web query:

How to fix TypeScript eslint disable comment in React components?

💡 Result:

To properly handle TypeScript ESLint disable comments in React components while maintaining code quality, follow these guidelines:

1. Configure ban-ts-comment Rule

Adjust the rule in your ESLint config to allow specific directives when necessary:

// eslint.config.js
export default tseslint.config({
  rules: {
    "@typescript-eslint/ban-ts-comment": [
      "error",
      {
        "ts-expect-error": "allow-with-description",
        "ts-ignore": true // defaults to false (disallow)
      }
    ]
  }
});

This configuration allows @ts-expect-error with descriptions but blocks @ts-ignore[1][4].

2. Valid Comment Format

Always include a description when suppressing errors:

// @ts-expect-error - Temporary workaround for API response type mismatch
const data = await fetchResponse<ExpectedType>();

3. React Component Usage

For JSX blocks, use block-style ESLint disable comments:

{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
// @ts-expect-error - Legacy component compatibility
<DeprecatedComponent />

4. Preferred Alternatives

Instead of directive comments:

  • Use type assertions sparingly:
    const element = document.getElementById('root') as HTMLElement;
  • Fix type declarations properly
  • Leverage TypeScript utility types (Partial, Omit, etc.)

5. Temporary Suppression

When absolutely necessary:

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - Remove after backend API V2 migration
fetchLegacyEndpoint();

Key Recommendations

  1. Audit Suppressions regularly (weekly/bi-weekly)
  2. Track Technical Debt using TODO comments with Jira/GitHub issue links
  3. Enable Type Checking in ESLint:
    // eslint.config.js
    export default tseslint.config({
      extends: [tseslint.configs.recommendedTypeChecked],
      parserOptions: {
        project: true,
        tsconfigRootDir: import.meta.dirname,
      },
    });

For React-specific patterns, combine with React ESLint rules:

// eslint.config.js
export default tseslint.config({
  extends: [
    tseslint.configs.recommended,
    'plugin:react/recommended'
  ],
  rules: {
    'react/jsx-key': 'error',
    '@typescript-eslint/naming-convention': [
      'error', 
      { selector: 'variable', format: ['PascalCase', 'camelCase'] }
    ]
  }
});

Citations:


Action Required: Address the TODO and Improve Type Suppression Handling

Please remove or update the TODO comment by replacing the temporary ESLint disable with a more explicit, descriptive fix. For example, instead of simply disabling the rule for "any", either refactor by providing a proper type definition for the header or add a descriptive suppression comment (using, for instance, @ts-expect-error - description) that clearly documents the temporary workaround. This will improve maintainability and allow for easier auditing of technical debt.

  • File: app/client/src/widgets/TableWidgetV2/component/header/TableColumnHeader.tsx (Lines 80-82)
  • Update Suggestion:
    Replace:
                {/* TODO: Fix this the next time the file is edited */}
                {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
                {headerGroup.headers.map((column: any, columnIndex: number) => {
    With a more descriptive, temporary fix such as:
                {/* TODO: Replace with proper type annotations for header columns */}
                {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
                {/* @ts-expect-error - Using 'any' until header type is defined */}
                {headerGroup.headers.map((column: any, columnIndex: number) => {
  • Additional Consideration: If possible, update the ESLint configuration to enforce descriptive error suppressions (see typescript-eslint/ban-ts-comment guidelines) and gradually eliminate the need for such suppressions.

AmanAgarwal041
AmanAgarwal041 previously approved these changes Mar 5, 2025
Copy link
Contributor

@AmanAgarwal041 AmanAgarwal041 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

jacquesikot
jacquesikot previously approved these changes Mar 6, 2025
…o rahulbarwal/tablev2-table-ui-components-refactor
@rahulbarwal rahulbarwal dismissed stale reviews from jacquesikot and AmanAgarwal041 via ed5a94f March 7, 2025 05:42
@rahulbarwal
Copy link
Contributor Author

/build-deploy-preview skip-tests=true

Copy link

github-actions bot commented Mar 7, 2025

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/13714575880.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 39367.
recreate: .

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 9c53de0 and ed5a94f.

📒 Files selected for processing (3)
  • app/client/src/widgets/TableWidgetV2/component/Table.tsx (6 hunks)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/VirtualList.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/client/src/widgets/TableWidgetV2/component/TableBodyCoreComponents/VirtualList.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: client-lint / client-lint
  • GitHub Check: client-unit-tests / client-unit-tests
  • GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
🔇 Additional comments (9)
app/client/src/widgets/TableWidgetV2/component/Table.tsx (4)

120-136: Good use of useMemo for performance optimization

The refactoring of pageCount and currentPageIndex into useMemo hooks with proper dependencies is a good performance optimization. This ensures calculations are only performed when relevant props change.


225-239: Well-structured memoization of UI visibility logic

The isHeaderVisible calculation has been properly extracted into a useMemo hook with appropriate dependencies, which improves performance and readability.


254-269: Consider addressing the TODO comment

The TODO comment suggests this code is intended for removal. Consider either removing it now or documenting why it's still needed and when it can be removed.


305-356: Good use of context provider for prop drilling reduction

The implementation of TableProvider to centralize state management is a good approach. This effectively reduces prop drilling and makes the component hierarchy easier to maintain.

The simplified rendering of TableHeader and conditional table type rendering also improves readability.

app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (5)

5-6: Good refactoring of imports

The import paths have been updated appropriately to match the new project structure.


10-12: Effective simplification of component props

Removing unnecessary props from the interface in favor of using the context is a good refactoring approach that reduces coupling between components.


16-24: Well-implemented use of central context

Using useAppsmithTable to access shared state is a cleaner approach than prop drilling. The destructuring with renamed variables (like subPage: rows) maintains compatibility with existing code while transitioning to the new naming convention.


30-30: Consistent naming update

The reference to nextPageClick instead of loadMoreFromEvaluations maintains functional consistency while improving naming clarity.


44-52: Consistent property updates

Properties have been correctly updated to use context values instead of direct props, completing the refactoring to use centralized state.

Comment on lines +241 to 245
props.isVisibleSearch ||
props.isVisibleFilters ||
props.isVisibleDownload ||
props.isVisiblePagination ||
props.allowAddNewRow;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove dangling boolean expression

This boolean expression has no effect - it's not assigned to any variable or used in any conditional logic.

-props.isVisibleSearch ||
-  props.isVisibleFilters ||
-  props.isVisibleDownload ||
-  props.isVisiblePagination ||
-  props.allowAddNewRow;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
props.isVisibleSearch ||
props.isVisibleFilters ||
props.isVisibleDownload ||
props.isVisiblePagination ||
props.allowAddNewRow;

@rahulbarwal
Copy link
Contributor Author

/build-deploy-preview skip-tests=true

Copy link

github-actions bot commented Mar 7, 2025

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/13714702722.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 39367.
recreate: .

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableBodyComponent.tsx (3)

11-29: Consider using React.memo for performance optimization.

The component is well-structured with ForwardRef, but adding memoization could help prevent unnecessary re-renders in a virtualized context where performance is critical.

-const VirtualTableBodyComponent = React.forwardRef(
+const VirtualTableBodyComponent = React.memo(React.forwardRef(
   (props: VirtualTableBodyComponentProps, ref: Ref<SimpleBar>) => {
     const { height, pageSize, subPage: rows, tableSizes } = useAppsmithTable();

     return (
       <div className="simplebar-content-wrapper">
         <FixedVirtualList
           height={height}
           innerElementType={props.innerElementType}
           itemCount={rows.length}
           outerRef={ref}
           pageSize={pageSize}
           rows={rows}
           tableSizes={tableSizes}
         />
       </div>
     );
   },
-);
+));

13-13: Add fallback handling for context values.

The destructured values from useAppsmithTable might be undefined if the component is rendered outside the expected context.

-  const { height, pageSize, subPage: rows, tableSizes } = useAppsmithTable();
+  const { height = 0, pageSize = 0, subPage: rows = [], tableSizes = {} } = useAppsmithTable();

31-32: Add component display name for better debugging.

Setting a display name helps with identifying components in React DevTools and error stacks.

+VirtualTableBodyComponent.displayName = "VirtualTableBodyComponent";
 export default VirtualTableBodyComponent;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between ed5a94f and fe09d8c.

📒 Files selected for processing (1)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableBodyComponent.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: perform-test / client-build / client-build
  • GitHub Check: perform-test / server-build / server-unit-tests
  • GitHub Check: client-unit-tests / client-unit-tests
  • GitHub Check: client-prettier / prettier-check
  • GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
  • GitHub Check: client-lint / client-lint
  • GitHub Check: client-build / client-build
🔇 Additional comments (2)
app/client/src/widgets/TableWidgetV2/component/VirtualTable/VirtualTableBodyComponent.tsx (2)

1-5: Appropriate imports for a virtual table component.

The imports cover React essentials, SimpleBar for scrolling, and the required custom components. Good use of explicit type imports.


7-9: Clean interface definition.

Interface is well-typed and focused on just the necessary props required by the component.

<FixedVirtualList
height={height}
innerElementType={props.innerElementType}
itemCount={rows.length}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure safe access to rows.length.

If rows is null or undefined, accessing rows.length will cause a runtime error.

-         itemCount={rows.length}
+         itemCount={rows?.length || 0}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
itemCount={rows.length}
itemCount={rows?.length || 0}

@rahulbarwal
Copy link
Contributor Author

/build-deploy-preview skip-tests=true

Copy link

github-actions bot commented Mar 7, 2025

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/13714840830.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 39367.
recreate: .

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (1)

17-24: Effective context-based refactor.

Destructuring the table parameters from useAppsmithTable() is a clean approach to reducing dependencies on parent props and enhancing maintainability. Ensure all usage sites correctly reference these destructured items and handle edge cases (e.g., totalRecordsCount remains defined).

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between fe09d8c and 1f8cc34.

📒 Files selected for processing (1)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: client-unit-tests / client-unit-tests
  • GitHub Check: client-lint / client-lint
  • GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
  • GitHub Check: client-build / client-build
  • GitHub Check: client-prettier / prettier-check
🔇 Additional comments (5)
app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (5)

5-6: Good use of new imports to centralize logic.

Importing FixedInfiniteVirtualList and useAppsmithTable helps consolidate table functionality and reduces prop drilling by relying on context-based data.


11-11: Inner element customization.

Exposing innerElementType in InfiniteScrollBodyProps is a neat addition that offers flexibility in rendering the table structure.


30-30: Naming consistency check.

Renaming loadMore to nextPageClick clarifies intent. Please verify that this updated function name is consistently referenced in related components and hooks throughout the codebase.


44-44: Height prop now sourced from context.

Using height={height} straight from the context is consistent and straightforward. No further issues here.


52-52: Contextual table sizing.

Passing tableSizes={tableSizes} from context aligns with the new approach for unified state. This is a good improvement over redundant props.

Copy link

github-actions bot commented Mar 7, 2025

Deploy-Preview-URL: https://ce-39367.dp.appsmith.com

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
app/client/src/widgets/TableWidgetV2/component/StaticTable/StaticTableBodyComponent.tsx (1)

13-13: Consider using a more stable key for row mapping.

Using array index as a key might cause rendering issues if rows are reordered or filtered dynamically.

- return <Row index={index} key={index} row={row} />;
+ return <Row index={index} key={row.id || index} row={row} />;
app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx (2)

8-8: Empty interface can be replaced with a type alias.

An empty interface is equivalent to {}. Consider using a type alias instead.

- interface StaticTableProps {}
+ type StaticTableProps = Record<string, never>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 7-8: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


9-9: Improve parameter naming in the StaticTable component.

Using underscore _ for props parameter can reduce code readability. Consider using a more descriptive name even if props aren't currently used.

- const StaticTable = (_: StaticTableProps, ref: React.Ref<SimpleBar>) => {
+ const StaticTable = (props: StaticTableProps, ref: React.Ref<SimpleBar>) => {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 1f8cc34 and 3cd678f.

📒 Files selected for processing (5)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/StaticTableBodyComponent.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTableBody.test.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx (1 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (3 hunks)
  • app/client/src/widgets/TableWidgetV2/component/VirtualTable/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/tests/StaticTableBody.test.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx

[error] 7-8: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

app/client/src/widgets/TableWidgetV2/component/VirtualTable/index.tsx

[error] 8-9: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: perform-test / client-build / client-build
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: perform-test / server-build / server-unit-tests
  • GitHub Check: client-unit-tests / client-unit-tests
  • GitHub Check: client-lint / client-lint
  • GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
  • GitHub Check: client-build / client-build
  • GitHub Check: client-prettier / prettier-check
🔇 Additional comments (12)
app/client/src/widgets/TableWidgetV2/component/StaticTable/StaticTableBodyComponent.tsx (1)

1-18: Clean implementation of the static table body component.

The component structure looks good. It correctly leverages the TableContext through useAppsmithTable hook to access necessary props and data, resulting in cleaner code with less prop drilling.

app/client/src/widgets/TableWidgetV2/component/StaticTable/index.tsx (1)

1-20: Effective implementation of the StaticTable component using context.

The component successfully utilizes React's context API through the useAppsmithTable hook to get the necessary styles and props. The use of SimpleBar for scrolling and proper component composition with TableColumnHeader and StaticTableBodyComponent is well-structured.

🧰 Tools
🪛 Biome (1.9.4)

[error] 7-8: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

app/client/src/widgets/TableWidgetV2/component/VirtualTable/index.tsx (4)

9-10: Remove unnecessary empty interface.

The empty interface VirtualTableProps serves no purpose and can be replaced with an empty object type.

-interface VirtualTableProps {}
-const VirtualTable = (_: VirtualTableProps, ref: React.Ref<SimpleBar>) => {
+const VirtualTable = (_: Record<string, never>, ref: React.Ref<SimpleBar>) => {

1-7: The imports look clean and appropriate.

The component correctly imports necessary dependencies and custom components for its functionality.


11-30: Good implementation of the virtual table component.

The component properly uses the context hook to determine rendering behavior and correctly implements conditional rendering based on the scroll mode. The use of SimpleBar and ref forwarding is appropriate for this use case.


32-32: Appropriate use of forwardRef.

Correctly exports the component with React.forwardRef to ensure ref is passed to the SimpleBar component.

app/client/src/widgets/TableWidgetV2/component/VirtualTable/InifiniteScrollBody/index.tsx (6)

5-6: Good refactoring to use context.

The import changes reflect the move to a context-based approach, which helps reduce prop drilling.


10-12: Props interface simplified appropriately.

The interface has been correctly simplified to only include necessary props, as the other properties are now accessed through the context.


14-24: Good implementation of context consumer.

The component has been renamed with the proper "Component" suffix for consistency, and now correctly uses the context hook to retrieve necessary values that were previously passed as props.


30-30: Property name update is consistent with context naming.

The nextPageClick property is now used instead of loadMoreFromEvaluations, maintaining consistency with the context API.


44-52: Props are correctly sourced from context.

The component now properly uses values from the context instead of props, which reduces prop drilling and makes the component more maintainable.


62-62: Consistent component export.

The export correctly uses the updated component name.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTable.test.tsx (1)

88-104: Consider using try/finally for spy restoration

While the current approach works, using try/finally would ensure the console spy is always restored, even if the test fails.

  it("throws error when used outside TableContext", () => {
    const consoleErrorSpy = jest
      .spyOn(console, "error")
      .mockImplementation(() => {});

    (useAppsmithTable as jest.Mock).mockImplementation(() => {
      throw new Error("useTable must be used within a TableProvider");
    });

    const ref = React.createRef<SimpleBar>();

-    expect(() => render(<StaticTable ref={ref} />)).toThrow(
-      "useTable must be used within a TableProvider",
-    );
-
-    consoleErrorSpy.mockRestore();
+    try {
+      expect(() => render(<StaticTable ref={ref} />)).toThrow(
+        "useTable must be used within a TableProvider",
+      );
+    } finally {
+      consoleErrorSpy.mockRestore();
+    }
  });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 3cd678f and a972f6d.

📒 Files selected for processing (1)
  • app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTable.test.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: client-lint / client-lint
  • GitHub Check: client-unit-tests / client-unit-tests
  • GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
  • GitHub Check: client-build / client-build
  • GitHub Check: client-prettier / prettier-check
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: perform-test / server-build / server-unit-tests
  • GitHub Check: perform-test / client-build / client-build
🔇 Additional comments (7)
app/client/src/widgets/TableWidgetV2/component/StaticTable/__tests__/StaticTable.test.tsx (7)

1-7: Good import structure

All necessary testing utilities and components are properly imported for the test file.


8-21: Well-implemented SimpleBar mock

The mock implementation correctly handles ref forwarding and preserves the required props interface, which is essential for testing the component behavior.


23-37: Appropriate dependency mocking

The test correctly mocks all required dependencies with test IDs, allowing for proper component isolation during testing.


39-53: Good test setup and teardown

The test suite properly initializes mock data and clears mocks between tests to prevent test contamination.


55-62: Correct rendering verification

This test effectively verifies that both header and body components are rendered as expected.


64-76: Thorough style application test

Good test that verifies the styles from context are correctly applied to the scroll container.


78-86: Robust ref forwarding test

The test properly verifies that the ref is forwarded to the correct element. The class check is an appropriate alternative to instanceof for DOM elements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ok-to-test Required label for CI Query & Widgets Pod All issues related to Query, JS, Eval, and Widgets skip-changelog Adding this label to a PR prevents it from being listed in the changelog Table Widget V2 Issues related to Table Widget V2 Widgets & Accelerators Pod Issues related to widgets & Accelerators Widgets Product This label groups issues related to widgets
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor table widget code base for clarity
3 participants