This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new f2e0cbbd0d9 Adds support for hiding columns in datatable (#47826)
f2e0cbbd0d9 is described below

commit f2e0cbbd0d9bd38c59b053199a37db1abfca99ca
Author: Aritra Basu <[email protected]>
AuthorDate: Wed Mar 26 19:37:37 2025 +0530

    Adds support for hiding columns in datatable (#47826)
    
    * Add support for hiding columns in datatable
    
    Closes: #47423
    
    Currently defaulting to always allow filtering
    
    * Moved filter to outside the table
    
    * Update hide condition
    
    * Rebased and used hasRow
    
    * Updated title
    
    * Add margin
---
 .../ui/src/components/DataTable/DataTable.tsx      | 28 ++++++---
 .../src/components/DataTable/FilterMenuButton.tsx  | 70 ++++++++++++++++++++++
 .../ui/src/components/DataTable/TableList.tsx      |  1 -
 .../airflow/ui/src/components/DataTable/types.ts   |  3 +-
 4 files changed, 92 insertions(+), 10 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx 
b/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx
index d4ed640a46f..8168ac28e45 100644
--- a/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx
@@ -16,27 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { HStack, Text } from "@chakra-ui/react";
+import { HStack, Spacer, Text } from "@chakra-ui/react";
 import {
   getCoreRowModel,
   getExpandedRowModel,
   getPaginationRowModel,
   useReactTable,
+  type VisibilityState,
   type OnChangeFn,
   type TableState as ReactTableState,
   type Row,
   type Table as TanStackTable,
   type Updater,
 } from "@tanstack/react-table";
-import React, { type ReactNode, useCallback, useRef } from "react";
+import React, { type ReactNode, useCallback, useRef, useState } from "react";
 
-import { ProgressBar, Pagination, Toaster } from "../ui";
-import { CardList } from "./CardList";
-import { TableList } from "./TableList";
-import { createSkeletonMock } from "./skeleton";
-import type { CardDef, MetaColumn, TableState } from "./types";
+import { CardList } from "src/components/DataTable/CardList";
+import FilterMenuButton from "src/components/DataTable/FilterMenuButton";
+import { TableList } from "src/components/DataTable/TableList";
+import { createSkeletonMock } from "src/components/DataTable/skeleton";
+import type { CardDef, MetaColumn, TableState } from 
"src/components/DataTable/types";
+import { ProgressBar, Pagination, Toaster } from "src/components/ui";
 
 type DataTableProps<TData> = {
+  readonly allowFiltering?: boolean;
   readonly cardDef?: CardDef<TData>;
   readonly columns: Array<MetaColumn<TData>>;
   readonly data: Array<TData>;
@@ -57,6 +60,7 @@ type DataTableProps<TData> = {
 const defaultGetRowCanExpand = () => false;
 
 export const DataTable = <TData,>({
+  allowFiltering = true,
   cardDef,
   columns,
   data,
@@ -83,6 +87,7 @@ export const DataTable = <TData,>({
 
         // Only use the controlled state
         const nextState = {
+          columnVisibility: next.columnVisibility,
           pagination: next.pagination,
           sorting: next.sorting,
         };
@@ -92,21 +97,24 @@ export const DataTable = <TData,>({
     },
     [onStateChange],
   );
+  const [columnVisibility, setColumnVisibility] = 
useState<VisibilityState>({});
 
   const rest = Boolean(isLoading) ? createSkeletonMock(displayMode, 
skeletonCount, columns) : {};
 
   const table = useReactTable({
     columns,
     data,
+    enableHiding: true,
     getCoreRowModel: getCoreRowModel(),
     getExpandedRowModel: getExpandedRowModel(),
     getPaginationRowModel: getPaginationRowModel(),
     getRowCanExpand,
     manualPagination: true,
     manualSorting: true,
+    onColumnVisibilityChange: setColumnVisibility,
     onStateChange: handleStateChange,
     rowCount: total,
-    state: initialState,
+    state: { ...initialState, columnVisibility },
     ...rest,
   });
 
@@ -125,6 +133,10 @@ export const DataTable = <TData,>({
     <>
       <ProgressBar size="xs" visibility={Boolean(isFetching) && 
!Boolean(isLoading) ? "visible" : "hidden"} />
       <Toaster />
+      <HStack>
+        <Spacer display="flow" />
+        {allowFiltering && hasRows && display === "table" ? <FilterMenuButton 
table={table} /> : undefined}
+      </HStack>
       {errorMessage}
       {hasRows && display === "table" ? <TableList table={table} /> : 
undefined}
       {hasRows && display === "card" && cardDef !== undefined ? (
diff --git 
a/airflow-core/src/airflow/ui/src/components/DataTable/FilterMenuButton.tsx 
b/airflow-core/src/airflow/ui/src/components/DataTable/FilterMenuButton.tsx
new file mode 100644
index 00000000000..cbc9855a922
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/components/DataTable/FilterMenuButton.tsx
@@ -0,0 +1,70 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { IconButton } from "@chakra-ui/react";
+import { flexRender, type Header, type Table } from "@tanstack/react-table";
+import { MdFilterList } from "react-icons/md";
+
+import { Menu } from "src/components/ui";
+import { Checkbox } from "src/components/ui/Checkbox";
+
+type Props<TData> = {
+  readonly table: Table<TData>;
+};
+
+const FilterMenuButton = <TData,>({ table }: Props<TData>) => (
+  <Menu.Root closeOnSelect={false} positioning={{ placement: "bottom" }}>
+    <Menu.Trigger asChild>
+      <IconButton
+        aria-label="Filter table columns"
+        margin={1}
+        padding={0}
+        title="Filter table columns"
+        variant="plain"
+      >
+        <MdFilterList size="1" />
+      </IconButton>
+    </Menu.Trigger>
+    <Menu.Content>
+      {table.getAllLeafColumns().map((column) => {
+        const text = flexRender(column.columnDef.header, {
+          column,
+          header: { column } as Header<TData, unknown>,
+          table,
+        });
+
+        return text?.toString ? (
+          <Menu.Item asChild key={column.id} value={column.id}>
+            <Checkbox
+              checked={column.getIsVisible()}
+              // At least one item needs to be visible
+              disabled={table.getVisibleFlatColumns().length < 2 && 
column.getIsVisible()}
+              onChange={() => {
+                column.toggleVisibility();
+              }}
+            >
+              {text}
+            </Checkbox>
+          </Menu.Item>
+        ) : undefined;
+      })}
+    </Menu.Content>
+  </Menu.Root>
+);
+
+export default FilterMenuButton;
diff --git a/airflow-core/src/airflow/ui/src/components/DataTable/TableList.tsx 
b/airflow-core/src/airflow/ui/src/components/DataTable/TableList.tsx
index 5e18fabc837..d2027dcc5a1 100644
--- a/airflow-core/src/airflow/ui/src/components/DataTable/TableList.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DataTable/TableList.tsx
@@ -35,7 +35,6 @@ export const TableList = <TData,>({ renderSubComponent, table 
}: DataTableProps<
             const sort = column.getIsSorted();
             const canSort = column.getCanSort();
             const text = flexRender(column.columnDef.header, getContext());
-
             let rightIcon;
 
             if (canSort) {
diff --git a/airflow-core/src/airflow/ui/src/components/DataTable/types.ts 
b/airflow-core/src/airflow/ui/src/components/DataTable/types.ts
index 50383f3624c..38a8b0e36b8 100644
--- a/airflow-core/src/airflow/ui/src/components/DataTable/types.ts
+++ b/airflow-core/src/airflow/ui/src/components/DataTable/types.ts
@@ -17,10 +17,11 @@
  * under the License.
  */
 import type { SimpleGridProps } from "@chakra-ui/react";
-import type { ColumnDef, PaginationState, SortingState } from 
"@tanstack/react-table";
+import type { ColumnDef, PaginationState, SortingState, VisibilityState } from 
"@tanstack/react-table";
 import type { ReactNode } from "react";
 
 export type TableState = {
+  columnVisibility?: VisibilityState;
   pagination: PaginationState;
   sorting: SortingState;
 };

Reply via email to