Repository: metabase/metabase
Pull request #54787: feat: customize edit view data grid
Author: login: spalt08
Created: 2025-03-06T11:55:11Z | Merged: 2025-03-12T15:14:08Z


<img width="1450" alt="image" src="https://github.com/user-attachments/assets/bbc48dc9-0227-40f4-bb2b-bd24e52fbbe4" />




Diff:
diff --git a/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.module.css b/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.module.css
index f6dc20f82678..d2c0b9dd74d6 100644
--- a/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.module.css
+++ b/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.module.css
@@ -8,3 +8,43 @@
   overflow: hidden;
   flex: 1 1 auto;
 }
+
+/** DataGrid overrides */
+.tableGrid {
+  /* Defined in pixels since `DataGrid` component prop `defaultRowHeight` is also in PX
+   * Should be in sync with TABLE_DATA_VIEW_HEADER_HEIGHT const
+   */
+  --editing-row-height: 32px;
+
+  grid-template-rows: none;
+}
+
+.tableRow {
+  border-bottom: 1px solid var(--mb-color-border);
+
+  &:hover .tableBodyCell {
+    background-color: var(--mb-base-color-blue-10);
+  }
+
+  &:hover .tableHeaderCell {
+    box-shadow: none;
+  }
+}
+
+.tableHeaderCell {
+  background-color: var(--mb-base-color-orion-5);
+  height: var(--editing-row-height);
+  border-right: 1px solid var(--mb-color-border);
+}
+
+.tableBodyCell {
+  background-color: var(--mb-color-bg-white);
+  border-right: 1px solid var(--mb-color-border);
+}
+
+.headerCellContainer {
+  height: var(--editing-row-height);
+  display: flex;
+  align-items: center;
+  padding: 0 0.75rem;
+}
diff --git a/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx b/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
index bef6aeda2ebe..fb950e8290d8 100644
--- a/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
+++ b/enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
@@ -2,6 +2,7 @@ import type { ColumnSizingState } from "@tanstack/react-table";
 import type React from "react";
 import { useCallback, useMemo } from "react";
 
+import { Ellipsified } from "metabase/core/components/Ellipsified";
 import {
   type ColumnOptions,
   DataGrid,
@@ -9,9 +10,11 @@ import {
   useDataGridInstance,
 } from "metabase/data-grid";
 import { formatValue } from "metabase/lib/formatting/value";
+import { Box } from "metabase/ui";
 import type { Dataset, RowValue, RowValues } from "metabase-types/api";
 
 import { EditingBodyCellConditional } from "./EditingBodyCell";
+import S from "./TableDataView.module.css";
 import type { UpdatedRowCellsHandlerParams } from "./types";
 import { useTableEditing } from "./use-table-editing";
 
@@ -20,6 +23,8 @@ type TableDataViewProps = {
   onCellValueUpdate: (params: UpdatedRowCellsHandlerParams) => void;
 };
 
+const TABLE_DATA_VIEW_HEADER_HEIGHT = 32;
+
 export const TableDataView = ({
   data,
   onCellValueUpdate,
@@ -46,6 +51,13 @@ export const TableDataView = ({
         accessorFn: (row: RowValues) => row[columnIndex],
         formatter: value => formatValue(value, { column }),
         wrap: false,
+        header: function EditingHeader() {
+          return (
+            <Box className={S.headerCellContainer}>
+              <Ellipsified>{column.display_name}</Ellipsified>
+            </Box>
+          );
+        },
         editingCell: cellContext => (
           <EditingBodyCellConditional
             cellContext={cellContext}
@@ -74,6 +86,7 @@ export const TableDataView = ({
     columnOrder,
     columnSizingMap,
     columnsOptions,
+    defaultRowHeight: TABLE_DATA_VIEW_HEADER_HEIGHT,
   });
 
   const handleCellClick = useCallback(
@@ -94,5 +107,24 @@ export const TableDataView = ({
     [onCellClickToEdit, editingCellId],
   );
 
-  return <DataGrid {...tableProps} onBodyCellClick={handleCellClick} />;
+  return (
+    <DataGrid
+      {...tableProps}
+      classNames={{
+        tableGrid: S.tableGrid,
+        headerCell: S.tableHeaderCell,
+        bodyCell: S.tableBodyCell,
+        row: S.tableRow,
+      }}
+      styles={{
+        // Overrides HEADER_HEIGHT JS const
+        row: { height: TABLE_DATA_VIEW_HEADER_HEIGHT },
+        // Overrides theme constants and default white bg
+        bodyCell: {
+          backgroundColor: undefined,
+        },
+      }}
+      onBodyCellClick={handleCellClick}
+    />
+  );
 };


Reviews:
user: login: stasgavrylov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T11:01:51Z
user: login: spalt08
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T11:13:20Z
user: login: spalt08
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T11:13:39Z
user: login: deniskaber
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T20:29:03Z
user: login: spalt08
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T13:05:07Z
user: login: stasgavrylov
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T09:45:00Z
user: login: stasgavrylov
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T09:45:02Z
user: login: spalt08
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:53:59Z
user: login: deniskaber
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:35:39Z

Review comments:
diff_hunk: @@ -13,3 +13,41 @@
     z-index: 5; /* needed to avoid outline truncation for the focused cell */
   }
 }
+
+/** DataGrid overrides */
+.tableGrid {
+  --editing-row-height: 32px;
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.module.css
user: login: stasgavrylov
body: If I'm not mistaken `rem`s are recommended units (in cases bigger than 1-2px)
created_at: 2025-03-07T10:54:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,6 +52,16 @@ export const TableDataView = ({
         wrap: false,
       };
 
+      options.header = function EditingHeader(_props) {
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: stasgavrylov
body: pls provide type definition for `_props` here and on line 65 too 🙏 

it's currently displayed as `any`.

also, is there any reason why those properties are defined separately? When written as part of `options` object declaration, you get those typings for free.
created_at: 2025-03-07T11:01:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,6 +52,16 @@ export const TableDataView = ({
         wrap: false,
       };
 
+      options.header = function EditingHeader(_props) {
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: spalt08
body: It is automatically typed, however I agree that definition inside object is better than a separate definition. Changed in 300140e

<img width="761" alt="image" src="https://github.com/user-attachments/assets/aa038749-0e94-476a-abe3-9d3236d11f76" />

created_at: 2025-03-10T11:13:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,3 +13,41 @@
     z-index: 5; /* needed to avoid outline truncation for the focused cell */
   }
 }
+
+/** DataGrid overrides */
+.tableGrid {
+  --editing-row-height: 32px;
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.module.css
user: login: spalt08
body: Left a CSS comment:
```
Defined in pixels since `DataGrid` component prop `defaultRowHeight` is also in PX
```
created_at: 2025-03-10T11:13:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,6 +50,15 @@ export const TableDataView = ({
         name: columnName,
         accessorFn: (row: RowValues) => row[columnIndex],
         wrap: false,
+        header: function EditingHeader() {
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: deniskaber
body: Can we do just extra styling using `classNames.headerCell` that you added in https://github.com/metabase/metabase/pull/54752 ? 
created_at: 2025-03-10T20:28:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -144,6 +154,19 @@ export const TableDataView = ({
   );
 
   return (
-    <DataGrid {...tableProps} onBodyCellDoubleClick={handleCellDoubleClick} />
+    <DataGrid
+      {...tableProps}
+      classNames={{
+        tableGrid: S.tableGrid,
+        headerCell: S.tableHeaderCell,
+        bodyCell: S.tableBodyCell,
+        row: S.tableRow,
+      }}
+      styles={{
+        // Overrides HEADER_HEIGHT JS const
+        row: { height: "32px" },
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: deniskaber
body: Should be moved to a constant
created_at: 2025-03-10T20:28:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,6 +50,15 @@ export const TableDataView = ({
         name: columnName,
         accessorFn: (row: RowValues) => row[columnIndex],
         wrap: false,
+        header: function EditingHeader() {
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: spalt08
body: @deniskaber It seems like no, since default header component uses pill-like component internally and it would be hard to fix only with styling
created_at: 2025-03-11T13:05:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -144,6 +154,19 @@ export const TableDataView = ({
   );
 
   return (
-    <DataGrid {...tableProps} onBodyCellDoubleClick={handleCellDoubleClick} />
+    <DataGrid
+      {...tableProps}
+      classNames={{
+        tableGrid: S.tableGrid,
+        headerCell: S.tableHeaderCell,
+        bodyCell: S.tableBodyCell,
+        row: S.tableRow,
+      }}
+      styles={{
+        // Overrides HEADER_HEIGHT JS const
+        row: { height: "32px" },
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: spalt08
body: Fixed
created_at: 2025-03-12T13:53:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -74,6 +87,7 @@ export const TableDataView = ({
     columnOrder,
     columnSizingMap,
     columnsOptions,
+    defaultRowHeight: 32,
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: deniskaber
body: ```suggestion
    defaultRowHeight: TABLE_DATA_VIEW_HEADER_HEIGHT,
```
created_at: 2025-03-12T14:32:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,6 +50,15 @@ export const TableDataView = ({
         accessorFn: (row: RowValues) => row[columnIndex],
         formatter: value => formatValue(value, { column }),
         wrap: false,
+        header: function EditingHeader() {
+          return (
+            <Box className={S.headerCellContainer}>
+              <Text style={{ textOverflow: "ellipsis", overflow: "hidden" }}>
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.tsx
user: login: deniskaber
body: Perhaps we can use `Ellipsified` component instead?
created_at: 2025-03-12T14:34:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,3 +8,41 @@
   overflow: hidden;
   flex: 1 1 auto;
 }
+
+/** DataGrid overrides */
+.tableGrid {
+  /* Defined in pixels since `DataGrid` component prop `defaultRowHeight` is also in PX */
+  --editing-row-height: 32px;
path: enterprise/frontend/src/metabase-enterprise/data_editing/tables/TableDataView.module.css
user: login: deniskaber
body: Please add a note that this should be in sync with `TABLE_DATA_VIEW_HEADER_HEIGHT` constant
created_at: 2025-03-12T14:35:24Z
author_association: CONTRIBUTOR

PR comments:
user: login: trunk-io[bot]
created_at: 2025-03-06T12:48:32Z
body: <!-- Trunk Test Analytics -->


[![Static Badge](https://img.shields.io/badge/14006_tests-45fdde61-purple)](https://app.trunk.io/metabase/flaky-tests/pr/54787?repo=metabase/metabase&commitHash=45fdde619d79353e266d439bb0a6b651cfa4a37f&utm_source=gh-comment) &ensp; [![Static Badge](https://img.shields.io/badge/0-failed-crimson)](https://app.trunk.io/metabase/flaky-tests/pr/54787?repo=metabase/metabase&commitHash=45fdde619d79353e266d439bb0a6b651cfa4a37f&utm_source=gh-comment&runConclusion=FAILURE) &ensp; [![Static Badge](https://img.shields.io/badge/0-flaky-red)](https://app.trunk.io/metabase/flaky-tests/pr/54787?repo=metabase/metabase&commitHash=45fdde619d79353e266d439bb0a6b651cfa4a37f&utm_source=gh-comment&runConclusion=FAILURE) &ensp; [![Static Badge](https://img.shields.io/badge/0-quarantined-yellow)](https://app.trunk.io/metabase/flaky-tests/pr/54787?repo=metabase/metabase&commitHash=45fdde619d79353e266d439bb0a6b651cfa4a37f&utm_source=gh-comment&runConclusion=QUARANTINED)


<sub>

[View Full Report ↗︎](https://app.trunk.io/metabase/flaky-tests/pr/54787?repo=metabase/metabase&commitHash=45fdde619d79353e266d439bb0a6b651cfa4a37f&utm_source=gh-comment) ⋅ [Docs](https://docs.trunk.io/flaky-tests?utm_source=gh-comment)

</sub>
author_association: NONE