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

vatsrahul1001 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 0f8d6f88b0e UI: Keep unnamed columns out of the table columns menu 
(#74774)
0f8d6f88b0e is described below

commit 0f8d6f88b0e983a7861ccc56b5b55cee15f6b0d9
Author: Yuseok Jo <[email protected]>
AuthorDate: Sun Oct 18 16:47:52 2026 +0900

    UI: Keep unnamed columns out of the table columns menu (#74774)
---
 .../DataTable/DataTable.columnVisibility.test.tsx  | 20 ++++++++++++++
 .../ui/src/components/DataTable/DataTable.tsx      | 20 +++++++++++++-
 .../Asset/AssetStateStore/AssetStateStore.tsx      |  1 +
 .../ui/src/pages/Connections/Connections.tsx       |  1 +
 .../src/airflow/ui/src/pages/DagRuns/DagRuns.tsx   |  1 +
 .../ui/src/pages/DagsList/DagsListColumns.test.tsx | 31 ++++++++++++++++++++++
 .../ui/src/pages/DagsList/DagsListColumns.tsx      |  4 +++
 .../ui/src/pages/TaskInstances/TaskInstances.tsx   |  1 +
 .../ui/src/pages/TaskStateStore/TaskStateStore.tsx |  1 +
 .../airflow/ui/src/pages/Variables/Variables.tsx   |  1 +
 .../src/airflow/ui/src/pages/XCom/XCom.tsx         |  1 +
 11 files changed, 81 insertions(+), 1 deletion(-)

diff --git 
a/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.columnVisibility.test.tsx
 
b/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.columnVisibility.test.tsx
index 72c1124eab7..8a7500e124b 100644
--- 
a/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.columnVisibility.test.tsx
+++ 
b/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.columnVisibility.test.tsx
@@ -54,4 +54,24 @@ describe("DataTable column visibility", () => {
     expect(screen.getByRole("columnheader", { name: "Name" 
})).toBeInTheDocument();
     expect(screen.queryByRole("columnheader", { name: "Added" })).toBeNull();
   });
+
+  it("shows a column that cannot be hidden even when stored as hidden", () => {
+    localStorage.setItem("dataTable:pinned:columnVisibility", JSON.stringify({ 
Added: false, name: false }));
+
+    render(
+      <DataTable
+        columns={[
+          { accessorKey: "name", cell: () => "Name", enableHiding: false, 
header: "Name" },
+          { cell: () => "Added", header: "Added", id: "Added" },
+        ]}
+        data={[{ name: "John Doe" }]}
+        modelName="pinned"
+        total={1}
+      />,
+      { wrapper: ChakraWrapper },
+    );
+
+    expect(screen.getByRole("columnheader", { name: "Name" 
})).toBeInTheDocument();
+    expect(screen.queryByRole("columnheader", { name: "Added" })).toBeNull();
+  });
 });
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 83151985ebd..e7783f11a3f 100644
--- a/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx
@@ -108,6 +108,21 @@ type DataTableProps<TData extends RowData> = {
   readonly totalEntriesLimit?: number;
 };
 
+// Columns with hiding disabled are left out of the columns menu, so a hidden 
state stored for them
+// (e.g. saved before hiding was disabled) could never be undone. Such columns 
always show.
+const buildColumnVisibility = <TData extends RowData>(
+  columns: Array<MetaColumn<TData>>,
+  visibility: ColumnVisibilityState,
+): ColumnVisibilityState => {
+  const unhideableIds = new Set(
+    columns
+      .filter((column) => column.enableHiding === false)
+      .map((column) => column.id ?? ("accessorKey" in column ? 
String(column.accessorKey) : undefined)),
+  );
+
+  return Object.fromEntries(Object.entries(visibility).filter(([id]) => 
!unhideableIds.has(id)));
+};
+
 export const DataTable = <TData extends RowData>({
   cardDef,
   columns,
@@ -187,7 +202,10 @@ export const DataTable = <TData extends RowData>({
   );
   // Stored visibility only covers columns that existed when it was saved, so 
columns added
   // later still need their default visibility.
-  const columnVisibility = { ...initialState?.columnVisibility, 
...storedColumnVisibility };
+  const columnVisibility = buildColumnVisibility(columns, {
+    ...initialState?.columnVisibility,
+    ...storedColumnVisibility,
+  });
 
   // An absent total means the endpoint gives no count (e.g. cursor 
pagination), which the heading
   // reflects by naming the model without a number. Everything else still 
needs a real number.
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
 
b/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
index bf4649d69d8..da27f134b42 100644
--- 
a/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
+++ 
b/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
@@ -133,6 +133,7 @@ const getColumns = ({
         <DeleteAssetStateStoreButton assetId={assetId} storeKey={original.key} 
/>
       </Flex>
     ),
+    enableHiding: false,
     enableSorting: false,
     header: "",
   },
diff --git a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx 
b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx
index 6279938f574..3d0f199c575 100644
--- a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx
@@ -128,6 +128,7 @@ const getColumns = ({
           <DeleteConnectionButton connectionId={original.connection_id} 
disabled={hasSelection} />
         </Flex>
       ),
+      enableHiding: false,
       enableSorting: false,
       header: "",
       meta: {
diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx
index ccc190c8e23..a9cc7c59194 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx
@@ -241,6 +241,7 @@ const runColumns = ({
         <DeleteRunButton dagRun={row.original} />
       </Flex>
     ),
+    enableHiding: false,
     enableSorting: false,
     header: "",
     meta: {
diff --git 
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsListColumns.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsListColumns.test.tsx
new file mode 100644
index 00000000000..a53c41d82cb
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsListColumns.test.tsx
@@ -0,0 +1,31 @@
+/*!
+ * 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 { describe, expect, it } from "vitest";
+
+import { createColumns } from "./DagsListColumns";
+
+describe("createColumns", () => {
+  it("lets only columns with a header be hidden, so the columns menu has no 
unnamed entries", () => {
+    const unnamedHideable = createColumns((key) => key, { multiTeam: true, 
showRecentTasks: true })
+      .filter((column) => column.enableHiding !== false && column.header === 
"")
+      .map((column) => ("accessorKey" in column ? column.accessorKey : 
column.id));
+
+    expect(unnamedHideable).toEqual([]);
+  });
+});
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsListColumns.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsListColumns.tsx
index 74bc5577ccc..ebd40300c15 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsListColumns.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsListColumns.tsx
@@ -184,6 +184,7 @@ export const createColumns = (
   {
     accessorKey: "is_paused",
     cell: PauseCell,
+    enableHiding: false,
     enableSorting: false,
     header: "",
     meta: {
@@ -237,12 +238,14 @@ export const createColumns = (
   {
     accessorKey: "pending_actions",
     cell: PendingActionsCell,
+    enableHiding: false,
     enableSorting: false,
     header: "",
   },
   {
     accessorKey: "trigger",
     cell: TriggerCell,
+    enableHiding: false,
     enableSorting: false,
     header: "",
   },
@@ -256,6 +259,7 @@ export const createColumns = (
   {
     accessorKey: "delete",
     cell: DeleteCell,
+    enableHiding: false,
     enableSorting: false,
     header: "",
   },
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
index 48e33005e3d..35d42da362e 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
@@ -294,6 +294,7 @@ const taskInstanceColumns = ({
         <DeleteTaskInstanceButton taskInstance={row.original} />
       </Flex>
     ),
+    enableHiding: false,
     enableSorting: false,
     header: "",
     meta: {
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskStateStore/TaskStateStore.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskStateStore/TaskStateStore.tsx
index e81e679ab85..3d84f4e7c5d 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskStateStore/TaskStateStore.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskStateStore/TaskStateStore.tsx
@@ -105,6 +105,7 @@ const getColumns = ({
         />
       </Flex>
     ),
+    enableHiding: false,
     enableSorting: false,
     header: "",
   },
diff --git a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx 
b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx
index 2dd6c20fac8..981bf7ddfe5 100644
--- a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx
@@ -134,6 +134,7 @@ const getColumns = ({
           <DeleteVariableButton deleteKey={original.key} 
disabled={hasSelection} />
         </Flex>
       ),
+      enableHiding: false,
       enableSorting: false,
       header: "",
       meta: {
diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx 
b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
index ed5e1f2d5e9..bbf4eba432a 100644
--- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
@@ -174,6 +174,7 @@ const getColumns = ({
         <DeleteXComButton xcom={original} />
       </Flex>
     ),
+    enableHiding: false,
     enableSorting: false,
     header: "",
   },

Reply via email to