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 f822744a0a1 Fix None child state rendering in UI (#67552)
f822744a0a1 is described below

commit f822744a0a1030d23f8889044b1a3e2ed5f54e88
Author: Aditya Patel <[email protected]>
AuthorDate: Thu Jun 4 14:03:22 2026 -0400

    Fix None child state rendering in UI (#67552)
    
    * Fix None child state rendering in UI
    
    * Apply UI lint formatting
    
    * Normalize None child states in grid API
    
    * Remove uv lockfile changes
    
    * Remove uv lockfile changes
---
 .../api_fastapi/core_api/datamodels/ui/grid.py     |  3 ++-
 .../api_fastapi/core_api/services/ui/grid.py       |  6 +++++-
 .../ui/src/components/TaskInstanceTooltip.test.tsx | 22 ++++++++++++++++++++++
 airflow-core/src/airflow/ui/src/theme.ts           |  1 +
 .../api_fastapi/core_api/routes/ui/test_grid.py    | 12 ++++++------
 5 files changed, 36 insertions(+), 8 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/grid.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/grid.py
index 70b0c590c3f..19278430d3f 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/grid.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/grid.py
@@ -18,6 +18,7 @@
 from __future__ import annotations
 
 from datetime import datetime
+from typing import Literal
 
 from airflow.api_fastapi.core_api.base import BaseModel
 from airflow.utils.state import TaskInstanceState
@@ -29,7 +30,7 @@ class LightGridTaskInstanceSummary(BaseModel):
     task_id: str
     task_display_name: str
     state: TaskInstanceState | None
-    child_states: dict[TaskInstanceState | None, int] | None
+    child_states: dict[TaskInstanceState | Literal["none"], int] | None
     min_start_date: datetime | None
     max_end_date: datetime | None
     dag_version_number: int | None = None
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/services/ui/grid.py 
b/airflow-core/src/airflow/api_fastapi/core_api/services/ui/grid.py
index 3ea7c5b21a4..bff2fd66296 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/services/ui/grid.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/services/ui/grid.py
@@ -122,12 +122,16 @@ def agg_state(states):
     return None
 
 
+def _serialize_child_states(child_states: Counter[Any]) -> dict[str, int]:
+    return {state if state is not None else "none": count for state, count in 
child_states.items()}
+
+
 def _get_aggs_for_node(summary: GridNodeAgg) -> dict[str, Any]:
     return {
         "state": agg_state(summary.child_states),
         "min_start_date": summary.min_start_date,
         "max_end_date": summary.max_end_date,
-        "child_states": dict(summary.child_states),
+        "child_states": _serialize_child_states(summary.child_states),
         "dag_version_number": summary.dag_version_number,
     }
 
diff --git 
a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx 
b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
index e131a129edb..34b75620d4f 100644
--- a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
@@ -195,6 +195,28 @@ describe("TaskInstanceTooltip", () => {
     expect(screen.getByText(/startDate/iu)).toBeInTheDocument();
   });
 
+  it("renders API-normalized none child state in the breakdown", () => {
+    const taskInstance: LightGridTaskInstanceSummary = {
+      child_states: { none: 2, success: 1 },
+      max_end_date: null,
+      min_start_date: null,
+      state: null,
+      task_display_name: "Mapped Task",
+      task_id: "mapped_task",
+    };
+
+    render(
+      <TaskInstanceTooltip open taskInstance={taskInstance}>
+        <span>trigger</span>
+      </TaskInstanceTooltip>,
+      { wrapper: Wrapper },
+    );
+
+    expect(screen.getByText(/2\s+common:states\.none/iu)).toBeInTheDocument();
+    expect(screen.queryByText(/^common:states\.None$/iu)).toBeNull();
+    
expect(screen.getByText(/1\s+common:states\.success/iu)).toBeInTheDocument();
+  });
+
   it("shows run ID when provided explicitly for grid summaries", () => {
     const taskInstance: LightGridTaskInstanceSummary = {
       child_states: null,
diff --git a/airflow-core/src/airflow/ui/src/theme.ts 
b/airflow-core/src/airflow/ui/src/theme.ts
index 232aa622e10..5dea56794a7 100644
--- a/airflow-core/src/airflow/ui/src/theme.ts
+++ b/airflow-core/src/airflow/ui/src/theme.ts
@@ -413,6 +413,7 @@ const defaultAirflowTheme: ThemingConfig = {
       deferred: generateSemanticTokens("purple"),
       scheduled: generateSemanticTokens("zinc"),
       none: generateSemanticTokens("gray"),
+      no_status: generateSemanticTokens("gray"),
       removed: generateSemanticTokens("slate"),
       // TAILWIND 4.0 COLORS
       red: generateSemanticTokens("red"),
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
index 0c8f4c17449..8700e7ce96d 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
@@ -837,7 +837,7 @@ class TestGetGridDataEndpoint:
 
         expected = [
             {
-                "child_states": {"None": 1},
+                "child_states": {"none": 1},
                 "dag_version_number": 1,
                 "task_id": "mapped_task_2",
                 "task_display_name": "mapped_task_2",
@@ -846,7 +846,7 @@ class TestGetGridDataEndpoint:
                 "state": None,
             },
             {
-                "child_states": {"success": 1, "running": 1, "None": 1},
+                "child_states": {"success": 1, "running": 1, "none": 1},
                 "dag_version_number": 1,
                 "max_end_date": "2024-12-30T01:02:03Z",
                 "min_start_date": "2024-12-30T01:00:00Z",
@@ -873,7 +873,7 @@ class TestGetGridDataEndpoint:
                 "min_start_date": None,
             },
             {
-                "child_states": {"None": 6},
+                "child_states": {"none": 6},
                 "dag_version_number": 1,
                 "task_id": "task_group",
                 "task_display_name": "task_group",
@@ -882,7 +882,7 @@ class TestGetGridDataEndpoint:
                 "state": None,
             },
             {
-                "child_states": {"None": 2},
+                "child_states": {"none": 2},
                 "dag_version_number": 1,
                 "task_id": "task_group.inner_task_group",
                 "task_display_name": "task_group.inner_task_group",
@@ -891,7 +891,7 @@ class TestGetGridDataEndpoint:
                 "state": None,
             },
             {
-                "child_states": {"None": 2},
+                "child_states": {"none": 2},
                 "dag_version_number": 1,
                 "task_id": 
"task_group.inner_task_group.inner_task_group_sub_task",
                 "task_display_name": "Inner Task Group Sub Task Label",
@@ -900,7 +900,7 @@ class TestGetGridDataEndpoint:
                 "state": None,
             },
             {
-                "child_states": {"None": 4},
+                "child_states": {"none": 4},
                 "dag_version_number": 1,
                 "task_id": "task_group.mapped_task",
                 "task_display_name": "task_group.mapped_task",

Reply via email to